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