You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
13 KiB
394 lines
13 KiB
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#include "Generator.h"
|
|
#include "Specification.h"
|
|
#include "Utilities.h"
|
|
|
|
using namespace std;
|
|
|
|
// Convert a file name into a string that can be used to guard the include file with #ifdef...
|
|
static string makeGuardString(const string& filename) {
|
|
string s;
|
|
s.resize(15 + filename.size());
|
|
s = "RENDERSCRIPT_";
|
|
for (char c : filename) {
|
|
if (c == '.') {
|
|
s += '_';
|
|
} else {
|
|
s += toupper(c);
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* Write #ifdef's that ensure that the specified version is present. If we're at the final version,
|
|
* add a check on a flag that can be set for internal builds. This enables us to keep supporting
|
|
* old APIs in the runtime code.
|
|
*/
|
|
static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info, unsigned int finalVersion) {
|
|
if (info.intSize == 32) {
|
|
*file << "#ifndef __LP64__\n";
|
|
} else if (info.intSize == 64) {
|
|
*file << "#ifdef __LP64__\n";
|
|
}
|
|
|
|
ostringstream checkMaxVersion;
|
|
if (info.maxVersion > 0) {
|
|
checkMaxVersion << "(";
|
|
if (info.maxVersion == finalVersion) {
|
|
checkMaxVersion << "defined(RS_DECLARE_EXPIRED_APIS) || ";
|
|
}
|
|
checkMaxVersion << "RS_VERSION <= " << info.maxVersion << ")";
|
|
}
|
|
|
|
if (info.minVersion <= 1) {
|
|
// No minimum
|
|
if (info.maxVersion > 0) {
|
|
*file << "#if !defined(RS_VERSION) || " << checkMaxVersion.str() << "\n";
|
|
}
|
|
} else {
|
|
*file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion << ")";
|
|
if (info.maxVersion > 0) {
|
|
*file << " && " << checkMaxVersion.str();
|
|
}
|
|
*file << ")\n";
|
|
}
|
|
}
|
|
|
|
static void writeVersionGuardEnd(GeneratedFile* file, VersionInfo info) {
|
|
if (info.minVersion > 1 || info.maxVersion != 0) {
|
|
*file << "#endif\n";
|
|
}
|
|
if (info.intSize != 0) {
|
|
*file << "#endif\n";
|
|
}
|
|
}
|
|
|
|
static void writeComment(GeneratedFile* file, const string& name, const string& briefComment,
|
|
const vector<string>& comment, bool addDeprecatedWarning,
|
|
bool closeBlock) {
|
|
if (briefComment.empty() && comment.size() == 0) {
|
|
return;
|
|
}
|
|
*file << "/*\n";
|
|
if (!briefComment.empty()) {
|
|
*file << " * " << name << ": " << briefComment << "\n";
|
|
*file << " *\n";
|
|
}
|
|
if (addDeprecatedWarning) {
|
|
*file << " * DEPRECATED. Do not use.\n";
|
|
*file << " *\n";
|
|
}
|
|
for (size_t ct = 0; ct < comment.size(); ct++) {
|
|
string s = stripHtml(comment[ct]);
|
|
s = stringReplace(s, "@", "");
|
|
if (!s.empty()) {
|
|
*file << " * " << s << "\n";
|
|
} else {
|
|
*file << " *\n";
|
|
}
|
|
}
|
|
if (closeBlock) {
|
|
*file << " */\n";
|
|
}
|
|
}
|
|
|
|
static void writeConstantComment(GeneratedFile* file, const Constant& constant) {
|
|
const string name = constant.getName();
|
|
writeComment(file, name, constant.getSummary(), constant.getDescription(),
|
|
constant.deprecated(), true);
|
|
}
|
|
|
|
static void writeConstantSpecification(GeneratedFile* file, const ConstantSpecification& spec) {
|
|
const Constant* constant = spec.getConstant();
|
|
VersionInfo info = spec.getVersionInfo();
|
|
writeVersionGuardStart(file, info, constant->getFinalVersion());
|
|
*file << "static const " << spec.getType() << " " << constant->getName()
|
|
<< " = " << spec.getValue() << ";\n\n";
|
|
writeVersionGuardEnd(file, info);
|
|
}
|
|
|
|
static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) {
|
|
const Type* type = spec.getType();
|
|
const string& typeName = type->getName();
|
|
const VersionInfo info = spec.getVersionInfo();
|
|
writeVersionGuardStart(file, info, type->getFinalVersion());
|
|
|
|
const string attribute =
|
|
makeAttributeTag(spec.getAttribute(), "", type->getDeprecatedApiLevel(),
|
|
type->getDeprecatedMessage());
|
|
*file << "typedef ";
|
|
switch (spec.getKind()) {
|
|
case SIMPLE:
|
|
*file << spec.getSimpleType() << attribute;
|
|
break;
|
|
case RS_OBJECT:
|
|
*file << "struct " << typeName << " _RS_OBJECT_DECL" << attribute;
|
|
break;
|
|
case ENUM: {
|
|
*file << "enum" << attribute << " ";
|
|
const string name = spec.getEnumName();
|
|
if (!name.empty()) {
|
|
*file << name << " ";
|
|
}
|
|
*file << "{\n";
|
|
|
|
const vector<string>& values = spec.getValues();
|
|
const vector<string>& valueComments = spec.getValueComments();
|
|
const size_t last = values.size() - 1;
|
|
for (size_t i = 0; i <= last; i++) {
|
|
*file << " " << values[i];
|
|
if (i != last) {
|
|
*file << ",";
|
|
}
|
|
if (valueComments.size() > i && !valueComments[i].empty()) {
|
|
*file << " // " << valueComments[i];
|
|
}
|
|
*file << "\n";
|
|
}
|
|
*file << "}";
|
|
break;
|
|
}
|
|
case STRUCT: {
|
|
*file << "struct" << attribute << " ";
|
|
const string name = spec.getStructName();
|
|
if (!name.empty()) {
|
|
*file << name << " ";
|
|
}
|
|
*file << "{\n";
|
|
|
|
const vector<string>& fields = spec.getFields();
|
|
const vector<string>& fieldComments = spec.getFieldComments();
|
|
for (size_t i = 0; i < fields.size(); i++) {
|
|
*file << " " << fields[i] << ";";
|
|
if (fieldComments.size() > i && !fieldComments[i].empty()) {
|
|
*file << " // " << fieldComments[i];
|
|
}
|
|
*file << "\n";
|
|
}
|
|
*file << "}";
|
|
break;
|
|
}
|
|
}
|
|
*file << " " << typeName << ";\n";
|
|
|
|
writeVersionGuardEnd(file, info);
|
|
*file << "\n";
|
|
}
|
|
|
|
static void writeTypeComment(GeneratedFile* file, const Type& type) {
|
|
const string name = type.getName();
|
|
writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
|
|
}
|
|
|
|
static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
|
|
const FunctionPermutation& permutation) {
|
|
Function* function = spec.getFunction();
|
|
writeVersionGuardStart(file, spec.getVersionInfo(), function->getFinalVersion());
|
|
|
|
// Write linkage info.
|
|
const auto inlineCodeLines = permutation.getInline();
|
|
if (inlineCodeLines.size() > 0) {
|
|
*file << "static inline ";
|
|
} else {
|
|
*file << "extern ";
|
|
}
|
|
|
|
// Write the return type.
|
|
auto ret = permutation.getReturn();
|
|
if (ret) {
|
|
*file << ret->rsType;
|
|
} else {
|
|
*file << "void";
|
|
}
|
|
|
|
*file << makeAttributeTag(spec.getAttribute(), spec.isOverloadable() ? "overloadable" : "",
|
|
function->getDeprecatedApiLevel(), function->getDeprecatedMessage());
|
|
*file << "\n";
|
|
|
|
// Write the function name.
|
|
*file << " " << permutation.getName() << "(";
|
|
const int offset = 4 + permutation.getName().size() + 1; // Size of above
|
|
|
|
// Write the arguments. We wrap on mulitple lines if a line gets too long.
|
|
int charsOnLine = offset;
|
|
bool hasGenerated = false;
|
|
for (auto p : permutation.getParams()) {
|
|
if (hasGenerated) {
|
|
*file << ",";
|
|
charsOnLine++;
|
|
}
|
|
ostringstream ps;
|
|
ps << p->rsType;
|
|
if (p->isOutParameter) {
|
|
ps << "*";
|
|
}
|
|
if (!p->specName.empty() && p->rsType != "...") {
|
|
ps << " " << p->specName;
|
|
}
|
|
const string s = ps.str();
|
|
if (charsOnLine + s.size() >= 100) {
|
|
*file << "\n" << string(offset, ' ');
|
|
charsOnLine = offset;
|
|
} else if (hasGenerated) {
|
|
*file << " ";
|
|
charsOnLine++;
|
|
}
|
|
*file << s;
|
|
charsOnLine += s.size();
|
|
hasGenerated = true;
|
|
}
|
|
// In C, if no parameters, we need to output void, e.g. fn(void).
|
|
if (!hasGenerated) {
|
|
*file << "void";
|
|
}
|
|
*file << ")";
|
|
|
|
// Write the inline code, if any.
|
|
if (inlineCodeLines.size() > 0) {
|
|
*file << " {\n";
|
|
for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
|
|
if (inlineCodeLines[ct].empty()) {
|
|
*file << "\n";
|
|
} else {
|
|
*file << " " << inlineCodeLines[ct] << "\n";
|
|
}
|
|
}
|
|
*file << "}\n";
|
|
} else {
|
|
*file << ";\n";
|
|
}
|
|
|
|
writeVersionGuardEnd(file, spec.getVersionInfo());
|
|
*file << "\n";
|
|
}
|
|
|
|
static void writeFunctionComment(GeneratedFile* file, const Function& function) {
|
|
// Write the generic documentation.
|
|
writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
|
|
function.deprecated(), false);
|
|
|
|
// Comment the parameters.
|
|
if (function.someParametersAreDocumented()) {
|
|
*file << " *\n";
|
|
*file << " * Parameters:\n";
|
|
for (auto p : function.getParameters()) {
|
|
if (!p->documentation.empty()) {
|
|
*file << " * " << p->name << ": " << p->documentation << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Comment the return type.
|
|
const string returnDoc = function.getReturnDocumentation();
|
|
if (!returnDoc.empty()) {
|
|
*file << " *\n";
|
|
*file << " * Returns: " << returnDoc << "\n";
|
|
}
|
|
|
|
*file << " */\n";
|
|
}
|
|
|
|
static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
|
|
// Write all the variants.
|
|
for (auto permutation : spec.getPermutations()) {
|
|
writeFunctionPermutation(file, spec, *permutation);
|
|
}
|
|
}
|
|
|
|
static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
|
|
const string headerFileName = specFile.getHeaderFileName();
|
|
|
|
// We generate one header file for each spec file.
|
|
GeneratedFile file;
|
|
if (!file.start(directory, headerFileName)) {
|
|
return false;
|
|
}
|
|
|
|
// Write the comments that start the file.
|
|
file.writeNotices();
|
|
writeComment(&file, headerFileName, specFile.getBriefDescription(),
|
|
specFile.getFullDescription(), false, true);
|
|
file << "\n";
|
|
|
|
// Write the ifndef that prevents the file from being included twice.
|
|
const string guard = makeGuardString(headerFileName);
|
|
file << "#ifndef " << guard << "\n";
|
|
file << "#define " << guard << "\n\n";
|
|
|
|
// Add lines that need to be put in "as is".
|
|
if (specFile.getVerbatimInclude().size() > 0) {
|
|
for (auto s : specFile.getVerbatimInclude()) {
|
|
file << s << "\n";
|
|
}
|
|
file << "\n";
|
|
}
|
|
|
|
/* Write the constants, types, and functions in the same order as
|
|
* encountered in the spec file.
|
|
*/
|
|
set<Constant*> documentedConstants;
|
|
for (auto spec : specFile.getConstantSpecifications()) {
|
|
Constant* constant = spec->getConstant();
|
|
if (documentedConstants.find(constant) == documentedConstants.end()) {
|
|
documentedConstants.insert(constant);
|
|
writeConstantComment(&file, *constant);
|
|
}
|
|
writeConstantSpecification(&file, *spec);
|
|
}
|
|
set<Type*> documentedTypes;
|
|
for (auto spec : specFile.getTypeSpecifications()) {
|
|
Type* type = spec->getType();
|
|
if (documentedTypes.find(type) == documentedTypes.end()) {
|
|
documentedTypes.insert(type);
|
|
writeTypeComment(&file, *type);
|
|
}
|
|
writeTypeSpecification(&file, *spec);
|
|
}
|
|
|
|
set<Function*> documentedFunctions;
|
|
for (auto spec : specFile.getFunctionSpecifications()) {
|
|
// Do not include internal APIs in the header files.
|
|
if (spec->isInternal()) {
|
|
continue;
|
|
}
|
|
Function* function = spec->getFunction();
|
|
if (documentedFunctions.find(function) == documentedFunctions.end()) {
|
|
documentedFunctions.insert(function);
|
|
writeFunctionComment(&file, *function);
|
|
}
|
|
writeFunctionSpecification(&file, *spec);
|
|
}
|
|
|
|
file << "#endif // " << guard << "\n";
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool generateHeaderFiles(const string& directory) {
|
|
bool success = true;
|
|
for (auto specFile : systemSpecification.getSpecFiles()) {
|
|
if (!writeHeaderFile(directory, *specFile)) {
|
|
success = false;
|
|
}
|
|
}
|
|
return success;
|
|
}
|