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