GenerateHeaderFiles.cpp revision 66fea24fb5f3a02b744a9c71ae0fc22c03c4fc6e
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 42static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info) { 43 if (info.intSize == 32) { 44 *file << "#ifndef __LP64__\n"; 45 } else if (info.intSize == 64) { 46 *file << "#ifdef __LP64__\n"; 47 } 48 49 if (info.minVersion <= 1) { 50 // No minimum 51 if (info.maxVersion > 0) { 52 *file << "#if !defined(RS_VERSION) || (RS_VERSION <= " << info.maxVersion << ")\n"; 53 } 54 } else { 55 if (info.maxVersion == 0) { 56 // No maximum 57 *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion << "))\n"; 58 } else { 59 *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion 60 << ") && (RS_VERSION <= " << info.maxVersion << "))\n"; 61 } 62 } 63} 64 65static void writeVersionGuardEnd(GeneratedFile* file, VersionInfo info) { 66 if (info.minVersion > 1 || info.maxVersion != 0) { 67 *file << "#endif\n"; 68 } 69 if (info.intSize != 0) { 70 *file << "#endif\n"; 71 } 72} 73 74static void writeComment(GeneratedFile* file, const string& name, const string& briefComment, 75 const vector<string>& comment, bool addDeprecatedWarning, 76 bool closeBlock) { 77 if (briefComment.empty() && comment.size() == 0) { 78 return; 79 } 80 *file << "/*\n"; 81 if (!briefComment.empty()) { 82 *file << " * " << name << ": " << briefComment << "\n"; 83 *file << " *\n"; 84 } 85 if (addDeprecatedWarning) { 86 *file << " * DEPRECATED. Do not use.\n"; 87 *file << " *\n"; 88 } 89 for (size_t ct = 0; ct < comment.size(); ct++) { 90 string s = stripHtml(comment[ct]); 91 s = stringReplace(s, "@", ""); 92 if (!s.empty()) { 93 *file << " * " << s << "\n"; 94 } else { 95 *file << " *\n"; 96 } 97 } 98 if (closeBlock) { 99 *file << " */\n"; 100 } 101} 102 103static void writeConstantComment(GeneratedFile* file, const Constant& constant) { 104 const string name = constant.getName(); 105 writeComment(file, name, constant.getSummary(), constant.getDescription(), 106 constant.deprecated(), true); 107} 108 109static void writeConstantSpecification(GeneratedFile* file, const ConstantSpecification& spec) { 110 VersionInfo info = spec.getVersionInfo(); 111 writeVersionGuardStart(file, info); 112 *file << "#define " << spec.getConstant()->getName() << " " << spec.getValue() << "\n\n"; 113 writeVersionGuardEnd(file, info); 114} 115 116static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) { 117 const string& typeName = spec.getType()->getName(); 118 const VersionInfo info = spec.getVersionInfo(); 119 writeVersionGuardStart(file, info); 120 switch (spec.getKind()) { 121 case SIMPLE: 122 *file << "typedef " << spec.getSimpleType() << " " << typeName << ";\n"; 123 break; 124 case ENUM: { 125 *file << "typedef enum "; 126 const string name = spec.getEnumName(); 127 if (!name.empty()) { 128 *file << name << " "; 129 } 130 *file << "{\n"; 131 132 const vector<string>& values = spec.getValues(); 133 const vector<string>& valueComments = spec.getValueComments(); 134 const size_t last = values.size() - 1; 135 for (size_t i = 0; i <= last; i++) { 136 *file << " " << values[i]; 137 if (i != last) { 138 *file << ","; 139 } 140 if (valueComments.size() > i && !valueComments[i].empty()) { 141 *file << " // " << valueComments[i]; 142 } 143 *file << "\n"; 144 } 145 *file << "} " << typeName << ";\n"; 146 break; 147 } 148 case STRUCT: { 149 *file << "typedef struct "; 150 const string name = spec.getStructName(); 151 if (!name.empty()) { 152 *file << name << " "; 153 } 154 *file << "{\n"; 155 156 const vector<string>& fields = spec.getFields(); 157 const vector<string>& fieldComments = spec.getFieldComments(); 158 for (size_t i = 0; i < fields.size(); i++) { 159 *file << " " << fields[i] << ";"; 160 if (fieldComments.size() > i && !fieldComments[i].empty()) { 161 *file << " // " << fieldComments[i]; 162 } 163 *file << "\n"; 164 } 165 *file << "} "; 166 const string attrib = spec.getAttrib(); 167 if (!attrib.empty()) { 168 *file << attrib << " "; 169 } 170 *file << typeName << ";\n"; 171 break; 172 } 173 } 174 writeVersionGuardEnd(file, info); 175 *file << "\n"; 176} 177 178static void writeTypeComment(GeneratedFile* file, const Type& type) { 179 const string name = type.getName(); 180 writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true); 181} 182 183static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec, 184 const FunctionPermutation& permutation) { 185 writeVersionGuardStart(file, spec.getVersionInfo()); 186 187 // Write linkage info. 188 const auto inlineCodeLines = permutation.getInline(); 189 if (inlineCodeLines.size() > 0) { 190 *file << "static inline "; 191 } else { 192 *file << "extern "; 193 } 194 195 // Write the return type. 196 auto ret = permutation.getReturn(); 197 if (ret) { 198 *file << ret->rsType; 199 } else { 200 *file << "void"; 201 } 202 203 // Write the attribute. 204 *file << " __attribute__(("; 205 const string attrib = spec.getAttribute(); 206 if (attrib.empty()) { 207 *file << "overloadable"; 208 } else if (attrib[0] == '=') { 209 /* If starts with an equal, we don't automatically add overloadable. 210 * This is because of the error we made defining rsUnpackColor8888(). 211 */ 212 *file << attrib.substr(1); 213 } else { 214 *file << attrib << ", overloadable"; 215 } 216 *file << "))\n"; 217 218 // Write the function name. 219 *file << " " << permutation.getName() << "("; 220 const int offset = 4 + permutation.getName().size() + 1; // Size of above 221 222 // Write the arguments. We wrap on mulitple lines if a line gets too long. 223 int charsOnLine = offset; 224 bool hasGenerated = false; 225 for (auto p : permutation.getParams()) { 226 if (hasGenerated) { 227 *file << ","; 228 charsOnLine++; 229 } 230 ostringstream ps; 231 ps << p->rsType; 232 if (p->isOutParameter) { 233 ps << "*"; 234 } 235 if (!p->specName.empty()) { 236 ps << " " << p->specName; 237 } 238 const string s = ps.str(); 239 if (charsOnLine + s.size() >= 100) { 240 *file << "\n" << string(offset, ' '); 241 charsOnLine = offset; 242 } else if (hasGenerated) { 243 *file << " "; 244 charsOnLine++; 245 } 246 *file << s; 247 charsOnLine += s.size(); 248 hasGenerated = true; 249 } 250 // In C, if no parameters, we need to output void, e.g. fn(void). 251 if (!hasGenerated) { 252 *file << "void"; 253 } 254 *file << ")"; 255 256 // Write the inline code, if any. 257 if (inlineCodeLines.size() > 0) { 258 *file << " {\n"; 259 for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) { 260 if (inlineCodeLines[ct].empty()) { 261 *file << "\n"; 262 } else { 263 *file << " " << inlineCodeLines[ct] << "\n"; 264 } 265 } 266 *file << "}\n"; 267 } else { 268 *file << ";\n"; 269 } 270 271 writeVersionGuardEnd(file, spec.getVersionInfo()); 272 *file << "\n"; 273} 274 275static void writeFunctionComment(GeneratedFile* file, const Function& function) { 276 // Write the generic documentation. 277 writeComment(file, function.getName(), function.getSummary(), function.getDescription(), 278 function.deprecated(), false); 279 280 // Comment the parameters. 281 if (function.someParametersAreDocumented()) { 282 *file << " *\n"; 283 *file << " * Parameters:\n"; 284 for (auto p : function.getParameters()) { 285 if (!p->documentation.empty()) { 286 *file << " * " << p->name << ": " << p->documentation << "\n"; 287 } 288 } 289 } 290 291 // Comment the return type. 292 const string returnDoc = function.getReturnDocumentation(); 293 if (!returnDoc.empty()) { 294 *file << " *\n"; 295 *file << " * Returns: " << returnDoc << "\n"; 296 } 297 298 *file << " */\n"; 299} 300 301static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) { 302 // Write all the variants. 303 for (auto permutation : spec.getPermutations()) { 304 writeFunctionPermutation(file, spec, *permutation); 305 } 306} 307 308static bool writeHeaderFile(const string& directory, const SpecFile& specFile) { 309 const string headerFileName = specFile.getHeaderFileName(); 310 311 // We generate one header file for each spec file. 312 GeneratedFile file; 313 if (!file.start(directory, headerFileName)) { 314 return false; 315 } 316 317 // Write the comments that start the file. 318 file.writeNotices(); 319 writeComment(&file, headerFileName, specFile.getBriefDescription(), 320 specFile.getFullDescription(), false, true); 321 file << "\n"; 322 323 // Write the ifndef that prevents the file from being included twice. 324 const string guard = makeGuardString(headerFileName); 325 file << "#ifndef " << guard << "\n"; 326 file << "#define " << guard << "\n\n"; 327 328 // Add lines that need to be put in "as is". 329 if (specFile.getVerbatimInclude().size() > 0) { 330 for (auto s : specFile.getVerbatimInclude()) { 331 file << s << "\n"; 332 } 333 file << "\n"; 334 } 335 336 /* Write the constants, types, and functions in the same order as 337 * encountered in the spec file. 338 */ 339 set<Constant*> documentedConstants; 340 for (auto spec : specFile.getConstantSpecifications()) { 341 Constant* constant = spec->getConstant(); 342 if (documentedConstants.find(constant) == documentedConstants.end()) { 343 documentedConstants.insert(constant); 344 writeConstantComment(&file, *constant); 345 } 346 writeConstantSpecification(&file, *spec); 347 } 348 set<Type*> documentedTypes; 349 for (auto spec : specFile.getTypeSpecifications()) { 350 Type* type = spec->getType(); 351 if (documentedTypes.find(type) == documentedTypes.end()) { 352 documentedTypes.insert(type); 353 writeTypeComment(&file, *type); 354 } 355 writeTypeSpecification(&file, *spec); 356 } 357 358 set<Function*> documentedFunctions; 359 for (auto spec : specFile.getFunctionSpecifications()) { 360 Function* function = spec->getFunction(); 361 if (documentedFunctions.find(function) == documentedFunctions.end()) { 362 documentedFunctions.insert(function); 363 writeFunctionComment(&file, *function); 364 } 365 writeFunctionSpecification(&file, *spec); 366 } 367 368 file << "#endif // " << guard << "\n"; 369 file.close(); 370 return true; 371} 372 373bool generateHeaderFiles(const string& directory) { 374 bool success = true; 375 for (auto specFile : systemSpecification.getSpecFiles()) { 376 if (!writeHeaderFile(directory, *specFile)) { 377 success = false; 378 } 379 } 380 return success; 381} 382