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