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 <iostream>
     18 #include <sstream>
     19 
     20 #include "Generator.h"
     21 #include "Specification.h"
     22 #include "Utilities.h"
     23 
     24 using namespace std;
     25 
     26 struct DetailedFunctionEntry {
     27     VersionInfo info;
     28     string htmlDeclaration;
     29 };
     30 
     31 static const char OVERVIEW_HTML_FILE_NAME[] = "overview.html";
     32 static const char OVERVIEW_JD_FILE_NAME[] = "overview.jd";
     33 static const char INDEX_HTML_FILE_NAME[] = "index.html";
     34 static const char INDEX_JD_FILE_NAME[] = "index.jd";
     35 
     36 static 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 
     60 static 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.
     68 static 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
     78 static 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 
     96 static 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 
    119 static 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  */
    152 static 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.
    174 static 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 
    200 static 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 
    226 static 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 
    236 static void writeSummaryTableEnd(GeneratedFile* file) {
    237     *file << "</tbody></table>\n";
    238 }
    239 
    240 enum DeprecatedSelector {
    241     DEPRECATED_ONLY,
    242     NON_DEPRECATED_ONLY,
    243     ALL,
    244 };
    245 
    246 static 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 
    272 static 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 
    286 static 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 
    309 static void writeHtmlVersionTag(GeneratedFile* file, VersionInfo info,
    310                                 bool addSpacing) {
    311     ostringstream stream;
    312     if (info.intSize == 32) {
    313         stream << "When compiling for 32 bits. ";
    314     } else if (info.intSize == 64) {
    315         stream << "When compiling for 64 bits. ";
    316     }
    317 
    318     if (info.minVersion > 1 || info.maxVersion) {
    319         const char* mid =
    320                     "<a "
    321                     "href='http://developer.android.com/guide/topics/manifest/"
    322                     "uses-sdk-element.html#ApiLevels'>API level ";
    323         if (info.minVersion <= 1) {
    324             // No minimum
    325             if (info.maxVersion > 0) {
    326                 stream << "Removed from " << mid << info.maxVersion + 1 << " and higher";
    327             }
    328         } else {
    329             if (info.maxVersion == 0) {
    330                 // No maximum
    331                 stream << "Added in " << mid << info.minVersion;
    332             } else {
    333                 stream << mid << info.minVersion << " - " << info.maxVersion;
    334             }
    335         }
    336         stream << "</a>";
    337     }
    338     string s = stream.str();
    339     // Remove any trailing whitespace
    340     while (s.back() == ' ') {
    341         s.pop_back();
    342     }
    343     if (!s.empty()) {
    344         *file << (addSpacing ? "    " : "") << s << "\n";
    345     }
    346 }
    347 
    348 static void writeDetailedTypeSpecification(GeneratedFile* file, const TypeSpecification* spec) {
    349     switch (spec->getKind()) {
    350         case SIMPLE: {
    351             Type* type = spec->getType();
    352             *file << "<p>A typedef of: " << spec->getSimpleType()
    353                   << makeAttributeTag(spec->getAttribute(), "", type->getDeprecatedApiLevel(),
    354                                       type->getDeprecatedMessage())
    355                   << "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    356             writeHtmlVersionTag(file, spec->getVersionInfo(), false);
    357             *file << "</p>\n";
    358             break;
    359         }
    360         case RS_OBJECT: {
    361             *file << "<p>";
    362             writeHtmlVersionTag(file, spec->getVersionInfo(), false);
    363             *file << "</p>\n";
    364             break;
    365         }
    366         case ENUM: {
    367             *file << "<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n";
    368             writeHtmlVersionTag(file, spec->getVersionInfo(), false);
    369             *file << "</p>\n";
    370 
    371             *file << "  <table class='jd-tagtable'><tbody>\n";
    372             const vector<string>& values = spec->getValues();
    373             const vector<string>& valueComments = spec->getValueComments();
    374             for (size_t i = 0; i < values.size(); i++) {
    375                 *file << "    <tr><th>" << values[i] << "</th><td>";
    376                 if (valueComments.size() > i) {
    377                     *file << valueComments[i];
    378                 }
    379                 *file << "</td></tr>\n";
    380             }
    381             *file << "  </tbody></table><br/>\n";
    382             break;
    383         }
    384         case STRUCT: {
    385             *file << "<p>A structure with the following fields:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    386             writeHtmlVersionTag(file, spec->getVersionInfo(), false);
    387             *file << "</p>\n";
    388 
    389             *file << "  <table class='jd-tagtable'><tbody>\n";
    390             const vector<string>& fields = spec->getFields();
    391             const vector<string>& fieldComments = spec->getFieldComments();
    392             for (size_t i = 0; i < fields.size(); i++) {
    393                 *file << "    <tr><th>" << fields[i] << "</th><td>";
    394                 if (fieldComments.size() > i && !fieldComments[i].empty()) {
    395                     *file << fieldComments[i];
    396                 }
    397                 *file << "</td></tr>\n";
    398             }
    399             *file << "  </tbody></table><br/>\n";
    400             break;
    401         }
    402     }
    403 }
    404 
    405 static void writeDetailedConstantSpecification(GeneratedFile* file, ConstantSpecification* c) {
    406     *file << "      <tr><td>";
    407     *file << "Value: " << c->getValue() << "\n";
    408     writeHtmlVersionTag(file, c->getVersionInfo(), true);
    409     *file << "      </td></tr>\n";
    410     *file << "<br/>\n";
    411 }
    412 
    413 static bool writeOverviewForFile(GeneratedFile* file, const SpecFile& specFile) {
    414     bool success = true;
    415     *file << "<h2>" << specFile.getBriefDescription() << "</h2>\n";
    416     if (!generateHtmlParagraphs(file, specFile.getFullDescription())) {
    417         success = false;
    418     }
    419 
    420     // Write the summary tables.
    421     // file << "<h2>Summary</h2>\n";
    422     writeSummaryTables(file, specFile.getDocumentedConstants(), specFile.getDocumentedTypes(),
    423                        specFile.getDocumentedFunctions(), NON_DEPRECATED_ONLY, false);
    424 
    425     return success;
    426 }
    427 
    428 static bool generateOverview(const string& directory, bool forVerification) {
    429     GeneratedFile file;
    430     if (!file.start(directory, forVerification ? OVERVIEW_HTML_FILE_NAME : OVERVIEW_JD_FILE_NAME)) {
    431         return false;
    432     }
    433     bool success = true;
    434 
    435     writeHeader(&file, forVerification, "Runtime API Reference");
    436 
    437     for (auto specFile : systemSpecification.getSpecFiles()) {
    438         if (!writeOverviewForFile(&file, *specFile)) {
    439             success = false;
    440         }
    441     }
    442 
    443     writeFooter(&file, forVerification);
    444     file.close();
    445     return success;
    446 }
    447 
    448 static bool generateAlphabeticalIndex(const string& directory, bool forVerification) {
    449     GeneratedFile file;
    450     if (!file.start(directory, forVerification ? INDEX_HTML_FILE_NAME : INDEX_JD_FILE_NAME)) {
    451         return false;
    452     }
    453     writeHeader(&file, forVerification, "Index");
    454 
    455     writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(),
    456                        systemSpecification.getFunctions(), NON_DEPRECATED_ONLY, true);
    457 
    458     writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(),
    459                        systemSpecification.getFunctions(), DEPRECATED_ONLY, true);
    460 
    461     writeFooter(&file, forVerification);
    462     file.close();
    463     return true;
    464 }
    465 
    466 static void writeDeprecatedWarning(GeneratedFile* file, Definition* definition) {
    467     if (definition->deprecated()) {
    468         *file << "    <p><b>Deprecated.</b>  ";
    469         string s = definition->getDeprecatedMessage();
    470         convertDocumentationRefences(&s);
    471         if (!s.empty()) {
    472             *file << s;
    473         } else {
    474             *file << "Do not use.";
    475         }
    476         *file << "</p>\n";
    477     }
    478 }
    479 
    480 static bool writeDetailedConstant(GeneratedFile* file, Constant* constant) {
    481     if (constant->hidden()) {
    482         return true;
    483     }
    484     const string& name = constant->getName();
    485 
    486     *file << "<a name='android_rs:" << name << "'></a>\n";
    487     *file << "<div class='jd-details'>\n";
    488     *file << "  <h4 class='jd-details-title'>\n";
    489     *file << "    <span class='sympad'>" << name << "</span>\n";
    490     *file << "    <span class='normal'>: " << constant->getSummary() << "</span>\n";
    491     *file << "  </h4>\n";
    492 
    493     *file << "  <div class='jd-details-descr'>\n";
    494     *file << "    <table class='jd-tagtable'><tbody>\n";
    495     auto specifications = constant->getSpecifications();
    496     bool addSeparator = specifications.size() > 1;
    497     for (auto spec : specifications) {
    498         if (addSeparator) {
    499             *file << "    <h5 class='jd-tagtitle'>Variant:</h5>\n";
    500         }
    501         writeDetailedConstantSpecification(file, spec);
    502     }
    503     *file << "    </tbody></table>\n";
    504     *file << "  </div>\n";
    505 
    506     *file << "    <div class='jd-tagdata jd-tagdescr'>\n";
    507 
    508     writeDeprecatedWarning(file, constant);
    509     if (!generateHtmlParagraphs(file, constant->getDescription())) {
    510         return false;
    511     }
    512     *file << "    </div>\n";
    513 
    514     *file << "</div>\n";
    515     *file << "\n";
    516     return true;
    517 }
    518 
    519 static bool writeDetailedType(GeneratedFile* file, Type* type) {
    520     if (type->hidden()) {
    521         return true;
    522     }
    523     const string& name = type->getName();
    524 
    525     *file << "<a name='android_rs:" << name << "'></a>\n";
    526     *file << "<div class='jd-details'>\n";
    527     *file << "  <h4 class='jd-details-title'>\n";
    528     *file << "    <span class='sympad'>" << name << "</span>\n";
    529     *file << "    <span class='normal'>: " << type->getSummary() << "</span>\n";
    530     *file << "  </h4>\n";
    531 
    532     *file << "  <div class='jd-details-descr'>\n";
    533     for (auto spec : type->getSpecifications()) {
    534         writeDetailedTypeSpecification(file, spec);
    535     }
    536 
    537     writeDeprecatedWarning(file, type);
    538     if (!generateHtmlParagraphs(file, type->getDescription())) {
    539         return false;
    540     }
    541 
    542     *file << "  </div>\n";
    543     *file << "</div>\n";
    544     *file << "\n";
    545     return true;
    546 }
    547 
    548 static bool writeDetailedFunction(GeneratedFile* file, Function* function) {
    549     if (function->hidden()) {
    550         return true;
    551     }
    552     const string& name = function->getName();
    553 
    554     *file << "<a name='android_rs:" << name << "'></a>\n";
    555     *file << "<div class='jd-details'>\n";
    556     *file << "  <h4 class='jd-details-title'>\n";
    557     *file << "    <span class='sympad'>" << name << "</span>\n";
    558     *file << "    <span class='normal'>: " << function->getSummary() << "</span>\n";
    559     *file << "  </h4>\n";
    560 
    561     *file << "  <div class='jd-details-descr'>\n";
    562     map<string, DetailedFunctionEntry> entries;
    563     if (!getUnifiedFunctionPrototypes(function, &entries)) {
    564         return false;
    565     }
    566     *file << "    <table class='jd-tagtable'><tbody>\n";
    567     for (auto i : entries) {
    568         *file << "      <tr>\n";
    569         *file << "        <td>" << i.second.htmlDeclaration << "</td>\n";
    570         *file << "        <td>";
    571         writeHtmlVersionTag(file, i.second.info, true);
    572         *file << "        </td>\n";
    573         *file << "      </tr>\n";
    574     }
    575     *file << "    </tbody></table>\n";
    576     *file << "  </div>\n";
    577 
    578     if (function->someParametersAreDocumented()) {
    579         *file << "  <div class='jd-tagdata'>";
    580         *file << "    <h5 class='jd-tagtitle'>Parameters</h5>\n";
    581         *file << "    <table class='jd-tagtable'><tbody>\n";
    582         for (ParameterEntry* p : function->getParameters()) {
    583             *file << "    <tr><th>" << p->name << "</th><td>" << p->documentation << "</td></tr>\n";
    584         }
    585         *file << "    </tbody></table>\n";
    586         *file << "  </div>\n";
    587     }
    588 
    589     string ret = function->getReturnDocumentation();
    590     if (!ret.empty()) {
    591         *file << "  <div class='jd-tagdata'>";
    592         *file << "    <h5 class='jd-tagtitle'>Returns</h5>\n";
    593         *file << "    <table class='jd-tagtable'><tbody>\n";
    594         *file << "    <tr><td>" << ret << "</td></tr>\n";
    595         *file << "    </tbody></table>\n";
    596         *file << "  </div>\n";
    597     }
    598 
    599     *file << "  <div class='jd-tagdata jd-tagdescr'>\n";
    600     writeDeprecatedWarning(file, function);
    601     if (!generateHtmlParagraphs(file, function->getDescription())) {
    602         return false;
    603     }
    604     *file << "  </div>\n";
    605 
    606     *file << "</div>\n";
    607     *file << "\n";
    608     return true;
    609 }
    610 
    611 static bool writeDetailedDocumentationFile(const string& directory, const SpecFile& specFile,
    612                                            bool forVerification) {
    613     if (!specFile.hasSpecifications()) {
    614         // This is true for rs_core.spec
    615         return true;
    616     }
    617 
    618     GeneratedFile file;
    619     const string fileName = stringReplace(specFile.getSpecFileName(), ".spec",
    620                                           forVerification ? ".html" : ".jd");
    621     if (!file.start(directory, fileName)) {
    622         return false;
    623     }
    624     bool success = true;
    625 
    626     string title = specFile.getBriefDescription();
    627     writeHeader(&file, forVerification, title);
    628 
    629     file << "<h2>Overview</h2>\n";
    630     if (!generateHtmlParagraphs(&file, specFile.getFullDescription())) {
    631         success = false;
    632     }
    633 
    634     // Write the summary tables.
    635     file << "<h2>Summary</h2>\n";
    636     const auto& constants = specFile.getDocumentedConstants();
    637     const auto& types = specFile.getDocumentedTypes();
    638     const auto& functions = specFile.getDocumentedFunctions();
    639 
    640     writeSummaryTables(&file, constants, types, functions, NON_DEPRECATED_ONLY, false);
    641     writeSummaryTables(&file, constants, types, functions, DEPRECATED_ONLY, false);
    642 
    643     // Write the full details of each constant, type, and function.
    644     if (!constants.empty()) {
    645         file << "<h2>Constants</h2>\n";
    646         for (auto i : constants) {
    647             if (!writeDetailedConstant(&file, i.second)) {
    648                 success = false;
    649             }
    650         }
    651     }
    652     if (!types.empty()) {
    653         file << "<h2>Types</h2>\n";
    654         for (auto i : types) {
    655             if (!writeDetailedType(&file, i.second)) {
    656                 success = false;
    657             }
    658         }
    659     }
    660     if (!functions.empty()) {
    661         file << "<h2>Functions</h2>\n";
    662         for (auto i : functions) {
    663             if (!writeDetailedFunction(&file, i.second)) {
    664                 success = false;
    665             }
    666         }
    667     }
    668 
    669     writeFooter(&file, forVerification);
    670     file.close();
    671 
    672     if (!success) {
    673         // If in error, write a final message to make it easier to figure out which file failed.
    674         cerr << fileName << ": Failed due to errors.\n";
    675     }
    676     return success;
    677 }
    678 
    679 static void generateSnippet(GeneratedFile* file, const string& fileName, const string& title) {
    680     const char offset[] = "                  ";
    681     *file << offset << "<li><a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/"
    682           << fileName << "\">\n";
    683     *file << offset << "  <span class=\"en\">" << title << "</span>\n";
    684     *file << offset << "</a></li>\n";
    685 }
    686 
    687 /* Generate a partial file of links that should be cut & pasted into the proper section of the
    688  * guide_toc.cs file.
    689  */
    690 static bool generateAndroidTableOfContentSnippet(const string& directory) {
    691     GeneratedFile file;
    692     if (!file.start(directory, "guide_toc.cs")) {
    693         return false;
    694     }
    695     file << "<!-- Copy and paste the following lines into the RenderScript section of\n";
    696     file << "     platform/frameworks/base/docs/html/guide/guide_toc.cs\n\n";
    697 
    698     const char offset[] = "              ";
    699     file << offset << "<li class=\"nav-section\">\n";
    700     file << offset << "  <div class=\"nav-section-header\">\n";
    701     file << offset << "    <a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/" <<
    702             OVERVIEW_HTML_FILE_NAME << "\">\n";
    703     file << offset << "      <span class=\"en\">Runtime API Reference</span>\n";
    704     file << offset << "    </a></div>\n";
    705     file << offset << "  <ul>\n";
    706 
    707     for (auto specFile : systemSpecification.getSpecFiles()) {
    708         if (specFile->hasSpecifications()) {
    709             const string fileName = stringReplace(specFile->getSpecFileName(), ".spec", ".html");
    710             generateSnippet(&file, fileName, specFile->getBriefDescription());
    711         }
    712     }
    713     generateSnippet(&file, INDEX_HTML_FILE_NAME, "Index");
    714 
    715     file << offset << "  </ul>\n";
    716     file << offset << "</li>\n";
    717 
    718     return true;
    719 }
    720 
    721 bool generateDocumentation(const string& directory, bool forVerification) {
    722     bool success = generateOverview(directory, forVerification) &&
    723                    generateAlphabeticalIndex(directory, forVerification) &&
    724                    generateAndroidTableOfContentSnippet(directory);
    725     for (auto specFile : systemSpecification.getSpecFiles()) {
    726         if (!writeDetailedDocumentationFile(directory, *specFile, forVerification)) {
    727             success = false;
    728         }
    729     }
    730     return success;
    731 }
    732