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