/*
* 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;
}