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