GenerateHeaderFiles.cpp revision 66fea24fb5f3a02b744a9c71ae0fc22c03c4fc6e
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
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,
75                         const vector<string>& comment, bool addDeprecatedWarning,
76                         bool closeBlock) {
77    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    }
85    if (addDeprecatedWarning) {
86        *file << " * DEPRECATED.  Do not use.\n";
87        *file << " *\n";
88    }
89    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
103static void writeConstantComment(GeneratedFile* file, const Constant& constant) {
104    const string name = constant.getName();
105    writeComment(file, name, constant.getSummary(), constant.getDescription(),
106                 constant.deprecated(), true);
107}
108
109static 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();
118    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
178static void writeTypeComment(GeneratedFile* file, const Type& type) {
179    const string name = type.getName();
180    writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
181}
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
275static void writeFunctionComment(GeneratedFile* file, const Function& function) {
276    // Write the generic documentation.
277    writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
278                 function.deprecated(), false);
279
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()) {
286                *file << " *   " << p->name << ": " << p->documentation << "\n";
287            }
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";
299}
300
301static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
302    // Write all the variants.
303    for (auto permutation : spec.getPermutations()) {
304        writeFunctionPermutation(file, spec, *permutation);
305    }
306}
307
308static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
309    const string headerFileName = specFile.getHeaderFileName();
310
311    // We generate one header file for each spec file.
312    GeneratedFile file;
313    if (!file.start(directory, headerFileName)) {
314        return false;
315    }
316
317    // Write the comments that start the file.
318    file.writeNotices();
319    writeComment(&file, headerFileName, specFile.getBriefDescription(),
320                 specFile.getFullDescription(), false, true);
321    file << "\n";
322
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     */
339    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);
347    }
348    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);
356    }
357
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);
366    }
367
368    file << "#endif // " << guard << "\n";
369    file.close();
370    return true;
371}
372
373bool generateHeaderFiles(const string& directory) {
374    bool success = true;
375    for (auto specFile : systemSpecification.getSpecFiles()) {
376        if (!writeHeaderFile(directory, *specFile)) {
377            success = false;
378        }
379    }
380    return success;
381}
382