GenerateDocumentation.cpp revision 247c598d96c2445ab1a4c3a887438ce8c7353c06
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 26struct DetailedFunctionEntry { 27 VersionInfo info; 28 string htmlDeclaration; 29}; 30 31static const char OVERVIEW_HTML_FILE_NAME[] = "overview.html"; 32static const char OVERVIEW_JD_FILE_NAME[] = "overview.jd"; 33static const char INDEX_HTML_FILE_NAME[] = "index.html"; 34static const char INDEX_JD_FILE_NAME[] = "index.jd"; 35 36static void writeHeader(GeneratedFile* file, bool forVerification, const string& title) { 37 if (forVerification) { 38 *file << "<!DOCTYPE html>\n"; 39 *file << "<!-- " << AUTO_GENERATED_WARNING << "-->\n"; 40 *file << "<html><head>\n" 41 "<title>RenderScript Reference</title>\n" 42 "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>\n" 43 "<link rel='stylesheet' " 44 "href='http://fonts.googleapis.com/css?family=Roboto+Condensed'>\n" 45 "<link rel='stylesheet' href='http://fonts.googleapis.com/" 46 "css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold' " 47 "title='roboto'>\n" 48 "<link href='default.css' rel='stylesheet' type='text/css'>\n" 49 "<link href='fullscreen.css' rel='stylesheet' class='fullscreen' " 50 "type='text/css'>\n" 51 "<body class='gc-documentation develop reference'>\n\n"; 52 *file << "<h1>" << title << "</h1>\n"; 53 } else { 54 *file << "page.title=RenderScript " << title << "\n\n"; 55 *file << "@jd:body\n\n"; 56 } 57 *file << "<div class='renderscript'>\n"; 58} 59 60static void writeFooter(GeneratedFile* file, bool forVerification) { 61 *file << "</div>\n"; 62 if (forVerification) { 63 *file << "</body></html>\n"; 64 } 65} 66 67// If prefix starts input, copy it to stream and remove it from input. 68static void skipPrefix(ostringstream* stream, string* input, const string& prefix) { 69 size_t size = prefix.size(); 70 if (input->compare(0, size, prefix) != 0) { 71 return; 72 } 73 input->erase(0, size); 74 *stream << prefix; 75} 76 77// Merge b into a. Returns true if successful 78static bool mergeVersionInfo(VersionInfo* a, const VersionInfo& b) { 79 if (a->intSize != b.intSize) { 80 cerr << "Error. We don't currently support versions that differ based on int size\n"; 81 return false; 82 } 83 if (b.minVersion != 0 && a->maxVersion == b.minVersion - 1) { 84 a->maxVersion = b.maxVersion; 85 } else if (b.maxVersion != 0 && a->minVersion == b.maxVersion + 1) { 86 a->minVersion = b.minVersion; 87 } else { 88 cerr << "Error. This code currently assume that all versions are contiguous. Don't know " 89 "how to merge versions (" << a->minVersion << " - " << a->maxVersion << ") and (" 90 << b.minVersion << " - " << b.maxVersion << ")\n"; 91 return false; 92 } 93 return true; 94} 95 96static string getHtmlStringForType(const ParameterDefinition& parameter) { 97 string s = parameter.rsType; 98 ostringstream stream; 99 skipPrefix(&stream, &s, "const "); 100 skipPrefix(&stream, &s, "volatile "); 101 bool endsWithAsterisk = s.size() > 0 && s[s.size() - 1] == '*'; 102 if (endsWithAsterisk) { 103 s.erase(s.size() - 1, 1); 104 } 105 106 string anchor = systemSpecification.getHtmlAnchor(s); 107 if (anchor.empty()) { 108 // Not a RenderScript specific type. 109 return parameter.rsType; 110 } else { 111 stream << anchor; 112 } 113 if (endsWithAsterisk) { 114 stream << "*"; 115 } 116 return stream.str(); 117} 118 119static string getDetailedHtmlDeclaration(const FunctionPermutation& permutation) { 120 ostringstream stream; 121 auto ret = permutation.getReturn(); 122 if (ret) { 123 stream << getHtmlStringForType(*ret); 124 } else { 125 stream << "void"; 126 } 127 stream << " " << permutation.getName() << "("; 128 bool needComma = false; 129 for (auto p : permutation.getParams()) { 130 if (needComma) { 131 stream << ", "; 132 } 133 stream << getHtmlStringForType(*p); 134 if (p->isOutParameter) { 135 stream << "*"; 136 } 137 if (!p->specName.empty()) { 138 stream << " " << p->specName; 139 } 140 needComma = true; 141 } 142 stream << ");\n"; 143 return stream.str(); 144} 145 146/* Some functions (like max) have changed implementations but not their 147 * declaration. We need to unify these so that we don't end up with entries 148 * like: 149 * char max(char a, char b); Removed from API level 20 150 * char max(char a, char b); Added to API level 20 151 */ 152static bool getUnifiedFunctionPrototypes(Function* function, 153 map<string, DetailedFunctionEntry>* entries) { 154 for (auto f : function->getSpecifications()) { 155 DetailedFunctionEntry entry; 156 entry.info = f->getVersionInfo(); 157 for (auto p : f->getPermutations()) { 158 entry.htmlDeclaration = getDetailedHtmlDeclaration(*p); 159 const string s = stripHtml(entry.htmlDeclaration); 160 auto i = entries->find(s); 161 if (i == entries->end()) { 162 entries->insert(pair<string, DetailedFunctionEntry>(s, entry)); 163 } else { 164 if (!mergeVersionInfo(&i->second.info, entry.info)) { 165 return false; 166 } 167 } 168 } 169 } 170 return true; 171} 172 173// Convert words starting with @ into HTML references. Returns false if error. 174static bool convertDocumentationRefences(string* s) { 175 bool success = true; 176 size_t end = 0; 177 for (;;) { 178 size_t start = s->find('@', end); 179 if (start == string::npos) { 180 break; 181 } 182 // Find the end of the identifier 183 end = start; 184 char c; 185 do { 186 c = (*s)[++end]; 187 } while (isalnum(c) || c == '_'); 188 189 const string id = s->substr(start + 1, end - start - 1); 190 string anchor = systemSpecification.getHtmlAnchor(id); 191 if (anchor.empty()) { 192 cerr << "Error: Can't convert the documentation reference @" << id << "\n"; 193 success = false; 194 } 195 s->replace(start, end - start, anchor); 196 } 197 return success; 198} 199 200static bool generateHtmlParagraphs(GeneratedFile* file, const vector<string>& description) { 201 bool inParagraph = false; 202 for (auto s : description) { 203 // Empty lines in the .spec marks paragraphs. 204 if (s.empty()) { 205 if (inParagraph) { 206 *file << "</p>\n"; 207 inParagraph = false; 208 } 209 } else { 210 if (!inParagraph) { 211 *file << "<p> "; 212 inParagraph = true; 213 } 214 } 215 if (!convertDocumentationRefences(&s)) { 216 return false; 217 } 218 *file << s << "\n"; 219 } 220 if (inParagraph) { 221 *file << "</p>\n"; 222 } 223 return true; 224} 225 226static void writeSummaryTableStart(GeneratedFile* file, const string& label, bool labelIsHeading) { 227 if (labelIsHeading) { 228 *file << "<h2 style='margin-bottom: 0px;'>" << label << "</h2>\n"; 229 } 230 *file << "<table class='jd-sumtable'><tbody>\n"; 231 if (!labelIsHeading) { 232 *file << " <tr><th colspan='2'>" << label << "</th></tr>\n"; 233 } 234} 235 236static void writeSummaryTableEnd(GeneratedFile* file) { 237 *file << "</tbody></table>\n"; 238} 239 240enum DeprecatedSelector { 241 DEPRECATED_ONLY, 242 NON_DEPRECATED_ONLY, 243 ALL, 244}; 245 246static void writeSummaryTableEntry(ostream* stream, Definition* definition, 247 DeprecatedSelector deprecatedSelector) { 248 if (definition->hidden()) { 249 return; 250 } 251 const bool deprecated = definition->deprecated(); 252 if ((deprecatedSelector == DEPRECATED_ONLY && !deprecated) || 253 (deprecatedSelector == NON_DEPRECATED_ONLY && deprecated)) { 254 return; 255 } 256 257 *stream << " <tr class='alt-color api apilevel-1'>\n"; 258 *stream << " <td class='jd-linkcol'>\n"; 259 *stream << " <a href='" << definition->getUrl() << "'>" << definition->getName() 260 << "</a>\n"; 261 *stream << " </td>\n"; 262 *stream << " <td class='jd-descrcol' width='100%'>\n"; 263 *stream << " "; 264 if (deprecated) { 265 *stream << "<b>Deprecated</b>. "; 266 } 267 *stream << definition->getSummary() << "\n"; 268 *stream << " </td>\n"; 269 *stream << " </tr>\n"; 270} 271 272static void writeSummaryTable(GeneratedFile* file, const ostringstream* entries, const char* name, 273 DeprecatedSelector deprecatedSelector, bool labelAsHeader) { 274 string s = entries->str(); 275 if (!s.empty()) { 276 string prefix; 277 if (deprecatedSelector == DEPRECATED_ONLY) { 278 prefix = "Deprecated "; 279 } 280 writeSummaryTableStart(file, prefix + name, labelAsHeader); 281 *file << s; 282 writeSummaryTableEnd(file); 283 } 284} 285 286static void writeSummaryTables(GeneratedFile* file, const map<string, Constant*>& constants, 287 const map<string, Type*>& types, 288 const map<string, Function*>& functions, 289 DeprecatedSelector deprecatedSelector, bool labelAsHeader) { 290 ostringstream constantStream; 291 for (auto e : constants) { 292 writeSummaryTableEntry(&constantStream, e.second, deprecatedSelector); 293 } 294 writeSummaryTable(file, &constantStream, "Constants", deprecatedSelector, labelAsHeader); 295 296 ostringstream typeStream; 297 for (auto e : types) { 298 writeSummaryTableEntry(&typeStream, e.second, deprecatedSelector); 299 } 300 writeSummaryTable(file, &typeStream, "Types", deprecatedSelector, labelAsHeader); 301 302 ostringstream functionStream; 303 for (auto e : functions) { 304 writeSummaryTableEntry(&functionStream, e.second, deprecatedSelector); 305 } 306 writeSummaryTable(file, &functionStream, "Functions", deprecatedSelector, labelAsHeader); 307} 308 309static void writeHtmlVersionTag(GeneratedFile* file, VersionInfo info) { 310 ostringstream stream; 311 if (info.intSize == 32) { 312 stream << "When compiling for 32 bits. "; 313 } else if (info.intSize == 64) { 314 stream << "When compiling for 64 bits. "; 315 } 316 317 if (info.minVersion > 1 || info.maxVersion) { 318 const char* mid = 319 "<a " 320 "href='http://developer.android.com/guide/topics/manifest/" 321 "uses-sdk-element.html#ApiLevels'>API level "; 322 if (info.minVersion <= 1) { 323 // No minimum 324 if (info.maxVersion > 0) { 325 stream << "Removed from " << mid << info.maxVersion + 1 << " and beyond"; 326 } 327 } else { 328 if (info.maxVersion == 0) { 329 // No maximum 330 stream << "Added in " << mid << info.minVersion; 331 } else { 332 stream << mid << info.minVersion << " - " << info.maxVersion; 333 } 334 } 335 stream << "</a>"; 336 } 337 const string s = stream.str(); 338 if (!s.empty()) { 339 *file << " " << s << "\n"; 340 } 341} 342 343static void writeDetailedTypeSpecification(GeneratedFile* file, const TypeSpecification* type) { 344 switch (type->getKind()) { 345 case SIMPLE: 346 *file << "<p>A typedef of: " << type->getSimpleType() 347 << " "; 348 writeHtmlVersionTag(file, type->getVersionInfo()); 349 *file << "</p>\n"; 350 break; 351 case ENUM: { 352 *file << "<p>An enum with the following values: \n"; 353 writeHtmlVersionTag(file, type->getVersionInfo()); 354 *file << "</p>\n"; 355 356 *file << " <table class='jd-tagtable'><tbody>\n"; 357 const vector<string>& values = type->getValues(); 358 const vector<string>& valueComments = type->getValueComments(); 359 for (size_t i = 0; i < values.size(); i++) { 360 *file << " <tr><th>" << values[i] << "</th><td>"; 361 if (valueComments.size() > i) { 362 *file << valueComments[i]; 363 } 364 *file << "</td></tr>\n"; 365 } 366 *file << " </tbody></table><br/>\n"; 367 break; 368 } 369 case STRUCT: { 370 *file << "<p>A structure with the following fields: "; 371 writeHtmlVersionTag(file, type->getVersionInfo()); 372 *file << "</p>\n"; 373 374 *file << " <table class='jd-tagtable'><tbody>\n"; 375 const vector<string>& fields = type->getFields(); 376 const vector<string>& fieldComments = type->getFieldComments(); 377 for (size_t i = 0; i < fields.size(); i++) { 378 *file << " <tr><th>" << fields[i] << "</th><td>"; 379 if (fieldComments.size() > i && !fieldComments[i].empty()) { 380 *file << fieldComments[i]; 381 } 382 *file << "</td></tr>\n"; 383 } 384 *file << " </tbody></table><br/>\n"; 385 break; 386 } 387 } 388} 389 390static void writeDetailedConstantSpecification(GeneratedFile* file, ConstantSpecification* c) { 391 *file << " <tr><td>"; 392 *file << "Value: " << c->getValue() << "\n"; 393 writeHtmlVersionTag(file, c->getVersionInfo()); 394 *file << " </td></tr>\n"; 395 *file << "<br/>\n"; 396} 397 398static bool writeOverviewForFile(GeneratedFile* file, const SpecFile& specFile) { 399 bool success = true; 400 *file << "<h2>" << specFile.getBriefDescription() << "</h2>\n"; 401 if (!generateHtmlParagraphs(file, specFile.getFullDescription())) { 402 success = false; 403 } 404 405 // Write the summary tables. 406 // file << "<h2>Summary</h2>\n"; 407 writeSummaryTables(file, specFile.getDocumentedConstants(), specFile.getDocumentedTypes(), 408 specFile.getDocumentedFunctions(), NON_DEPRECATED_ONLY, false); 409 410 return success; 411} 412 413static bool generateOverview(const string& directory, bool forVerification) { 414 GeneratedFile file; 415 if (!file.start(directory, forVerification ? OVERVIEW_HTML_FILE_NAME : OVERVIEW_JD_FILE_NAME)) { 416 return false; 417 } 418 bool success = true; 419 420 writeHeader(&file, forVerification, "Runtime API Reference"); 421 422 for (auto specFile : systemSpecification.getSpecFiles()) { 423 if (!writeOverviewForFile(&file, *specFile)) { 424 success = false; 425 } 426 } 427 428 writeFooter(&file, forVerification); 429 file.close(); 430 return success; 431} 432 433static bool generateAlphabeticalIndex(const string& directory, bool forVerification) { 434 GeneratedFile file; 435 if (!file.start(directory, forVerification ? INDEX_HTML_FILE_NAME : INDEX_JD_FILE_NAME)) { 436 return false; 437 } 438 writeHeader(&file, forVerification, "Index"); 439 440 writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(), 441 systemSpecification.getFunctions(), NON_DEPRECATED_ONLY, true); 442 443 writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(), 444 systemSpecification.getFunctions(), DEPRECATED_ONLY, true); 445 446 writeFooter(&file, forVerification); 447 file.close(); 448 return true; 449} 450 451static void writeDeprecatedWarning(GeneratedFile* file, Definition* definition) { 452 if (definition->deprecated()) { 453 *file << " <p><b>Deprecated.</b> "; 454 string s = definition->getDeprecatedMessage(); 455 convertDocumentationRefences(&s); 456 if (!s.empty()) { 457 *file << s; 458 } else { 459 *file << "Do not use."; 460 } 461 *file << "</p>\n"; 462 } 463} 464 465static bool writeDetailedConstant(GeneratedFile* file, Constant* constant) { 466 if (constant->hidden()) { 467 return true; 468 } 469 const string& name = constant->getName(); 470 471 *file << "<a name='android_rs:" << name << "'></a>\n"; 472 *file << "<div class='jd-details'>\n"; 473 *file << " <h4 class='jd-details-title'>\n"; 474 *file << " <span class='sympad'>" << name << "</span>\n"; 475 *file << " <span class='normal'>: " << constant->getSummary() << "</span>\n"; 476 *file << " </h4>\n"; 477 478 *file << " <div class='jd-details-descr'>\n"; 479 *file << " <table class='jd-tagtable'><tbody>\n"; 480 auto specifications = constant->getSpecifications(); 481 bool addSeparator = specifications.size() > 1; 482 for (auto spec : specifications) { 483 if (addSeparator) { 484 *file << " <h5 class='jd-tagtitle'>Variant:</h5>\n"; 485 } 486 writeDetailedConstantSpecification(file, spec); 487 } 488 *file << " </tbody></table>\n"; 489 *file << " </div>\n"; 490 491 *file << " <div class='jd-tagdata jd-tagdescr'>\n"; 492 493 writeDeprecatedWarning(file, constant); 494 if (!generateHtmlParagraphs(file, constant->getDescription())) { 495 return false; 496 } 497 *file << " </div>\n"; 498 499 *file << "</div>\n"; 500 *file << "\n"; 501 return true; 502} 503 504static bool writeDetailedType(GeneratedFile* file, Type* type) { 505 if (type->hidden()) { 506 return true; 507 } 508 const string& name = type->getName(); 509 510 *file << "<a name='android_rs:" << name << "'></a>\n"; 511 *file << "<div class='jd-details'>\n"; 512 *file << " <h4 class='jd-details-title'>\n"; 513 *file << " <span class='sympad'>" << name << "</span>\n"; 514 *file << " <span class='normal'>: " << type->getSummary() << "</span>\n"; 515 *file << " </h4>\n"; 516 517 *file << " <div class='jd-details-descr'>\n"; 518 for (auto spec : type->getSpecifications()) { 519 writeDetailedTypeSpecification(file, spec); 520 } 521 522 writeDeprecatedWarning(file, type); 523 if (!generateHtmlParagraphs(file, type->getDescription())) { 524 return false; 525 } 526 527 *file << " </div>\n"; 528 *file << "</div>\n"; 529 *file << "\n"; 530 return true; 531} 532 533static bool writeDetailedFunction(GeneratedFile* file, Function* function) { 534 if (function->hidden()) { 535 return true; 536 } 537 const string& name = function->getName(); 538 539 *file << "<a name='android_rs:" << name << "'></a>\n"; 540 *file << "<div class='jd-details'>\n"; 541 *file << " <h4 class='jd-details-title'>\n"; 542 *file << " <span class='sympad'>" << name << "</span>\n"; 543 *file << " <span class='normal'>: " << function->getSummary() << "</span>\n"; 544 *file << " </h4>\n"; 545 546 *file << " <div class='jd-details-descr'>\n"; 547 map<string, DetailedFunctionEntry> entries; 548 if (!getUnifiedFunctionPrototypes(function, &entries)) { 549 return false; 550 } 551 *file << " <table class='jd-tagtable'><tbody>\n"; 552 for (auto i : entries) { 553 *file << " <tr>\n"; 554 *file << " <td>" << i.second.htmlDeclaration << "</td>\n"; 555 *file << " <td>"; 556 writeHtmlVersionTag(file, i.second.info); 557 *file << " </td>\n"; 558 *file << " </tr>\n"; 559 } 560 *file << " </tbody></table>\n"; 561 *file << " </div>\n"; 562 563 if (function->someParametersAreDocumented()) { 564 *file << " <div class='jd-tagdata'>"; 565 *file << " <h5 class='jd-tagtitle'>Parameters</h5>\n"; 566 *file << " <table class='jd-tagtable'><tbody>\n"; 567 for (ParameterEntry* p : function->getParameters()) { 568 *file << " <tr><th>" << p->name << "</th><td>" << p->documentation << "</td></tr>\n"; 569 } 570 *file << " </tbody></table>\n"; 571 *file << " </div>\n"; 572 } 573 574 string ret = function->getReturnDocumentation(); 575 if (!ret.empty()) { 576 *file << " <div class='jd-tagdata'>"; 577 *file << " <h5 class='jd-tagtitle'>Returns</h5>\n"; 578 *file << " <table class='jd-tagtable'><tbody>\n"; 579 *file << " <tr><td>" << ret << "</td></tr>\n"; 580 *file << " </tbody></table>\n"; 581 *file << " </div>\n"; 582 } 583 584 *file << " <div class='jd-tagdata jd-tagdescr'>\n"; 585 writeDeprecatedWarning(file, function); 586 if (!generateHtmlParagraphs(file, function->getDescription())) { 587 return false; 588 } 589 *file << " </div>\n"; 590 591 *file << "</div>\n"; 592 *file << "\n"; 593 return true; 594} 595 596static bool writeDetailedDocumentationFile(const string& directory, const SpecFile& specFile, 597 bool forVerification) { 598 if (!specFile.hasSpecifications()) { 599 // This is true for rs_core.spec 600 return true; 601 } 602 603 GeneratedFile file; 604 const string fileName = stringReplace(specFile.getSpecFileName(), ".spec", 605 forVerification ? ".html" : ".jd"); 606 if (!file.start(directory, fileName)) { 607 return false; 608 } 609 bool success = true; 610 611 string title = specFile.getBriefDescription(); 612 writeHeader(&file, forVerification, title); 613 614 file << "<h2>Overview</h2>\n"; 615 if (!generateHtmlParagraphs(&file, specFile.getFullDescription())) { 616 success = false; 617 } 618 619 // Write the summary tables. 620 file << "<h2>Summary</h2>\n"; 621 const auto& constants = specFile.getDocumentedConstants(); 622 const auto& types = specFile.getDocumentedTypes(); 623 const auto& functions = specFile.getDocumentedFunctions(); 624 625 writeSummaryTables(&file, constants, types, functions, NON_DEPRECATED_ONLY, false); 626 writeSummaryTables(&file, constants, types, functions, DEPRECATED_ONLY, false); 627 628 // Write the full details of each constant, type, and function. 629 if (!constants.empty()) { 630 file << "<h2>Constants</h2>\n"; 631 for (auto i : constants) { 632 if (!writeDetailedConstant(&file, i.second)) { 633 success = false; 634 } 635 } 636 } 637 if (!types.empty()) { 638 file << "<h2>Types</h2>\n"; 639 for (auto i : types) { 640 if (!writeDetailedType(&file, i.second)) { 641 success = false; 642 } 643 } 644 } 645 if (!functions.empty()) { 646 file << "<h2>Functions</h2>\n"; 647 for (auto i : functions) { 648 if (!writeDetailedFunction(&file, i.second)) { 649 success = false; 650 } 651 } 652 } 653 654 writeFooter(&file, forVerification); 655 file.close(); 656 657 if (!success) { 658 // If in error, write a final message to make it easier to figure out which file failed. 659 cerr << fileName << ": Failed due to errors.\n"; 660 } 661 return success; 662} 663 664static void generateSnippet(GeneratedFile* file, const string& fileName, const string& title) { 665 const char offset[] = " "; 666 *file << offset << "<li><a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/" 667 << fileName << "\">\n"; 668 *file << offset << " <span class=\"en\">" << title << "</span>\n"; 669 *file << offset << "</a></li>\n"; 670} 671 672/* Generate a partial file of links that should be cut & pasted into the proper section of the 673 * guide_toc.cs file. 674 */ 675static bool generateAndroidTableOfContentSnippet(const string& directory) { 676 GeneratedFile file; 677 if (!file.start(directory, "guide_toc.cs")) { 678 return false; 679 } 680 file << "<!-- Copy and paste the following lines into the RenderScript section of\n"; 681 file << " platform/frameworks/base/docs/html/guide/guide_toc.cs\n\n"; 682 683 const char offset[] = " "; 684 file << offset << "<li class=\"nav-section\">\n"; 685 file << offset << " <div class=\"nav-section-header\">\n"; 686 file << offset << " <a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/" << 687 OVERVIEW_HTML_FILE_NAME << "\">\n"; 688 file << offset << " <span class=\"en\">Runtime API Reference</span>\n"; 689 file << offset << " </a></div>\n"; 690 file << offset << " <ul>\n"; 691 692 for (auto specFile : systemSpecification.getSpecFiles()) { 693 if (specFile->hasSpecifications()) { 694 const string fileName = stringReplace(specFile->getSpecFileName(), ".spec", ".html"); 695 generateSnippet(&file, fileName, specFile->getBriefDescription()); 696 } 697 } 698 generateSnippet(&file, INDEX_HTML_FILE_NAME, "Index"); 699 700 file << offset << " </ul>\n"; 701 file << offset << "</li>\n"; 702 703 return true; 704} 705 706bool generateDocumentation(const string& directory, bool forVerification) { 707 bool success = generateOverview(directory, forVerification) && 708 generateAlphabeticalIndex(directory, forVerification) && 709 generateAndroidTableOfContentSnippet(directory); 710 for (auto specFile : systemSpecification.getSpecFiles()) { 711 if (!writeDetailedDocumentationFile(directory, *specFile, forVerification)) { 712 success = false; 713 } 714 } 715 return success; 716} 717