2dc84ad3ef
Added global enum support. That exposed one big hole in bookmaker: The topic overview should not be in the class or struct if the topic includes multiple objects, which is the case for SkImageInfo and enums like SkColorType. This straightens that out, and then used that knowledge to strengthen the topics in SkRect as a test. Now SkRect has more groups of methods, and can expose and link to sets of methods with the same name. This work also is getting ready for tightening SeeAlso data, to be checked as part of the bots' tasks soon. Also, remove links from markup for lowercase method names unless the reference has trailing parentheses. TBR=caryclark@google.com Docs-Preview: https://skia.org/?cl=98782 Bug: skia:6898 Change-Id: I35419c9789da17e272047bf7b9c95b1cf44bb7fe Reviewed-on: https://skia-review.googlesource.com/98782 Commit-Queue: Cary Clark <caryclark@google.com> Reviewed-by: Cary Clark <caryclark@skia.org> Reviewed-by: Cary Clark <caryclark@google.com>
373 lines
10 KiB
C++
373 lines
10 KiB
C++
/*
|
|
* Copyright 2017 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "bookmaker.h"
|
|
#include "SkOSFile.h"
|
|
#include "SkOSPath.h"
|
|
|
|
static void debug_out(int len, const char* data) {
|
|
// convenient place to intercept arbitrary output
|
|
SkDebugf("%.*s", len, data);
|
|
}
|
|
|
|
bool ParserCommon::parseFile(const char* fileOrPath, const char* suffix) {
|
|
if (!sk_isdir(fileOrPath)) {
|
|
if (!this->parseFromFile(fileOrPath)) {
|
|
SkDebugf("failed to parse %s\n", fileOrPath);
|
|
return false;
|
|
}
|
|
} else {
|
|
SkOSFile::Iter it(fileOrPath, suffix);
|
|
for (SkString file; it.next(&file); ) {
|
|
SkString p = SkOSPath::Join(fileOrPath, file.c_str());
|
|
const char* hunk = p.c_str();
|
|
if (!SkStrEndsWith(hunk, suffix)) {
|
|
continue;
|
|
}
|
|
if (!this->parseFromFile(hunk)) {
|
|
SkDebugf("failed to parse %s\n", hunk);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ParserCommon::parseStatus(const char* statusFile, const char* suffix, StatusFilter filter) {
|
|
StatusIter iter(statusFile, suffix, filter);
|
|
if (iter.empty()) {
|
|
return false;
|
|
}
|
|
for (string file; iter.next(&file); ) {
|
|
SkString p = SkOSPath::Join(iter.baseDir().c_str(), file.c_str());
|
|
const char* hunk = p.c_str();
|
|
if (!this->parseFromFile(hunk)) {
|
|
SkDebugf("failed to parse %s\n", hunk);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ParserCommon::parseSetup(const char* path) {
|
|
sk_sp<SkData> data = SkData::MakeFromFileName(path);
|
|
if (nullptr == data.get()) {
|
|
SkDebugf("%s missing\n", path);
|
|
return false;
|
|
}
|
|
const char* rawText = (const char*) data->data();
|
|
bool hasCR = false;
|
|
size_t dataSize = data->size();
|
|
for (size_t index = 0; index < dataSize; ++index) {
|
|
if ('\r' == rawText[index]) {
|
|
hasCR = true;
|
|
break;
|
|
}
|
|
}
|
|
string name(path);
|
|
if (hasCR) {
|
|
vector<char> lfOnly;
|
|
for (size_t index = 0; index < dataSize; ++index) {
|
|
char ch = rawText[index];
|
|
if ('\r' == rawText[index]) {
|
|
ch = '\n';
|
|
if ('\n' == rawText[index + 1]) {
|
|
++index;
|
|
}
|
|
}
|
|
lfOnly.push_back(ch);
|
|
}
|
|
fLFOnly[name] = lfOnly;
|
|
dataSize = lfOnly.size();
|
|
rawText = &fLFOnly[name].front();
|
|
}
|
|
fRawData[name] = data;
|
|
fStart = rawText;
|
|
fLine = rawText;
|
|
fChar = rawText;
|
|
fEnd = rawText + dataSize;
|
|
fFileName = string(path);
|
|
fLineCount = 1;
|
|
return true;
|
|
}
|
|
|
|
void ParserCommon::writeBlockIndent(int size, const char* data) {
|
|
while (size && ' ' >= data[size - 1]) {
|
|
--size;
|
|
}
|
|
bool newLine = false;
|
|
while (size) {
|
|
while (size && ' ' > data[0]) {
|
|
++data;
|
|
--size;
|
|
}
|
|
if (!size) {
|
|
return;
|
|
}
|
|
if (newLine) {
|
|
this->lf(1);
|
|
}
|
|
TextParser parser(fFileName, data, data + size, fLineCount);
|
|
const char* lineEnd = parser.strnchr('\n', data + size);
|
|
int len = lineEnd ? (int) (lineEnd - data) : size;
|
|
this->writePending();
|
|
this->indentToColumn(fIndent);
|
|
if (fDebugOut) {
|
|
debug_out(len, data);
|
|
}
|
|
fprintf(fOut, "%.*s", len, data);
|
|
size -= len;
|
|
data += len;
|
|
newLine = true;
|
|
}
|
|
}
|
|
|
|
bool ParserCommon::writeBlockTrim(int size, const char* data) {
|
|
if (fOutdentNext) {
|
|
fIndent -= 4;
|
|
fOutdentNext = false;
|
|
}
|
|
while (size && ' ' >= data[0]) {
|
|
++data;
|
|
--size;
|
|
}
|
|
while (size && ' ' >= data[size - 1]) {
|
|
--size;
|
|
}
|
|
if (size <= 0) {
|
|
fLastChar = '\0';
|
|
return false;
|
|
}
|
|
SkASSERT(size < 16000);
|
|
if (size > 3 && !strncmp("#end", data, 4)) {
|
|
fMaxLF = 1;
|
|
}
|
|
if (this->leadingPunctuation(data, (size_t) size)) {
|
|
fPendingSpace = 0;
|
|
}
|
|
this->writePending();
|
|
if (fDebugOut) {
|
|
debug_out(size, data);
|
|
}
|
|
fprintf(fOut, "%.*s", size, data);
|
|
int added = 0;
|
|
fLastChar = data[size - 1];
|
|
while (size > 0 && '\n' != data[--size]) {
|
|
++added;
|
|
}
|
|
fColumn = size ? added : fColumn + added;
|
|
fSpaces = 0;
|
|
fLinefeeds = 0;
|
|
fMaxLF = added > 2 && !strncmp("#if", &data[size + (size > 0)], 3) ? 1 : 2;
|
|
if (fOutdentNext) {
|
|
fIndent -= 4;
|
|
fOutdentNext = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ParserCommon::writePending() {
|
|
fPendingLF = SkTMin(fPendingLF, fMaxLF);
|
|
bool wroteLF = false;
|
|
while (fLinefeeds < fPendingLF) {
|
|
if (fDebugOut) {
|
|
SkDebugf("\n");
|
|
}
|
|
fprintf(fOut, "\n");
|
|
++fLinefeeds;
|
|
wroteLF = true;
|
|
}
|
|
fPendingLF = 0;
|
|
if (wroteLF) {
|
|
SkASSERT(0 == fColumn);
|
|
SkASSERT(fIndent >= fSpaces);
|
|
if (fDebugOut) {
|
|
SkDebugf("%*s", fIndent - fSpaces, "");
|
|
}
|
|
fprintf(fOut, "%*s", fIndent - fSpaces, "");
|
|
fColumn = fIndent;
|
|
fSpaces = fIndent;
|
|
}
|
|
for (int index = 0; index < fPendingSpace; ++index) {
|
|
if (fDebugOut) {
|
|
SkDebugf(" ");
|
|
}
|
|
fprintf(fOut, " ");
|
|
++fColumn;
|
|
}
|
|
fPendingSpace = 0;
|
|
}
|
|
|
|
void ParserCommon::writeString(const char* str) {
|
|
const size_t len = strlen(str);
|
|
SkASSERT(len > 0);
|
|
SkASSERT(' ' < str[0]);
|
|
fLastChar = str[len - 1];
|
|
SkASSERT(' ' < fLastChar);
|
|
SkASSERT(!strchr(str, '\n'));
|
|
if (this->leadingPunctuation(str, strlen(str))) {
|
|
fPendingSpace = 0;
|
|
}
|
|
this->writePending();
|
|
if (fDebugOut) {
|
|
debug_out((int) strlen(str), str);
|
|
}
|
|
fprintf(fOut, "%s", str);
|
|
fColumn += len;
|
|
fSpaces = 0;
|
|
fLinefeeds = 0;
|
|
fMaxLF = 2;
|
|
}
|
|
|
|
const char* ParserCommon::ReadToBuffer(string filename, int* size) {
|
|
FILE* file = fopen(filename.c_str(), "rb");
|
|
if (!file) {
|
|
return nullptr;
|
|
}
|
|
fseek(file, 0L, SEEK_END);
|
|
*size = (int) ftell(file);
|
|
rewind(file);
|
|
char* buffer = new char[*size];
|
|
memset(buffer, ' ', *size);
|
|
SkAssertResult(*size == (int)fread(buffer, 1, *size, file));
|
|
fclose(file);
|
|
fflush(file);
|
|
return buffer;
|
|
}
|
|
|
|
bool ParserCommon::writtenFileDiffers(string filename, string readname) {
|
|
int writtenSize, readSize;
|
|
const char* written = ReadToBuffer(filename, &writtenSize);
|
|
if (!written) {
|
|
return true;
|
|
}
|
|
const char* read = ReadToBuffer(readname, &readSize);
|
|
if (!read) {
|
|
delete[] written;
|
|
return true;
|
|
}
|
|
#if 0 // enable for debugging this
|
|
int smaller = SkTMin(writtenSize, readSize);
|
|
for (int index = 0; index < smaller; ++index) {
|
|
if (written[index] != read[index]) {
|
|
SkDebugf("%.*s\n", 40, &written[index]);
|
|
SkDebugf("%.*s\n", 40, &read[index]);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (readSize != writtenSize) {
|
|
return true;
|
|
}
|
|
bool result = !!memcmp(written, read, writtenSize);
|
|
delete[] written;
|
|
delete[] read;
|
|
return result;
|
|
}
|
|
|
|
StatusIter::StatusIter(const char* statusFile, const char* suffix, StatusFilter filter)
|
|
: fSuffix(suffix)
|
|
, fFilter(filter) {
|
|
if (!this->parseFromFile(statusFile)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const char* block_names[] = {
|
|
"Completed",
|
|
"InProgress",
|
|
};
|
|
|
|
string StatusIter::baseDir() {
|
|
SkASSERT(fStack.back().fObject.isArray());
|
|
SkASSERT(fStack.size() > 2);
|
|
string dir;
|
|
for (unsigned index = 2; index < fStack.size(); ++index) {
|
|
dir += fStack[index].fName;
|
|
if (index < fStack.size() - 1) {
|
|
dir += SkOSPath::SEPARATOR;
|
|
}
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
// FIXME: need to compare fBlockName against fFilter
|
|
// need to compare fSuffix against next value returned
|
|
bool StatusIter::next(string* str) {
|
|
JsonStatus* status;
|
|
do {
|
|
do {
|
|
if (fStack.empty()) {
|
|
return false;
|
|
}
|
|
status = &fStack.back();
|
|
if (status->fIter != status->fObject.end()) {
|
|
break;
|
|
}
|
|
fStack.pop_back();
|
|
} while (true);
|
|
if (1 == fStack.size()) {
|
|
do {
|
|
StatusFilter blockType = StatusFilter::kUnknown;
|
|
for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
|
|
if (status->fIter.key().asString() == block_names[index]) {
|
|
blockType = (StatusFilter) index;
|
|
break;
|
|
}
|
|
}
|
|
if (blockType <= fFilter) {
|
|
break;
|
|
}
|
|
status->fIter++;
|
|
} while (status->fIter != status->fObject.end());
|
|
if (status->fIter == status->fObject.end()) {
|
|
continue;
|
|
}
|
|
}
|
|
if (!status->fObject.isArray()) {
|
|
SkASSERT(status->fIter != status->fObject.end());
|
|
JsonStatus block = {
|
|
*status->fIter,
|
|
status->fIter->begin(),
|
|
status->fIter.key().asString()
|
|
};
|
|
fStack.emplace_back(block);
|
|
status = &(&fStack.back())[-1];
|
|
status->fIter++;
|
|
status = &fStack.back();
|
|
continue;
|
|
}
|
|
*str = status->fIter->asString();
|
|
status->fIter++;
|
|
if (str->length() - strlen(fSuffix) == str->find(fSuffix)) {
|
|
return true;
|
|
}
|
|
} while (true);
|
|
return true;
|
|
}
|
|
|
|
bool StatusIter::parseFromFile(const char* path) {
|
|
sk_sp<SkData> json(SkData::MakeFromFileName(path));
|
|
if (!json) {
|
|
SkDebugf("file %s:\n", path);
|
|
return this->reportError<bool>("file not readable");
|
|
}
|
|
Json::Reader reader;
|
|
const char* data = (const char*)json->data();
|
|
if (!reader.parse(data, data+json->size(), fRoot)) {
|
|
SkDebugf("file %s:\n", path);
|
|
return this->reportError<bool>("file not parsable");
|
|
}
|
|
JsonStatus block = { fRoot, fRoot.begin(), "" };
|
|
fStack.emplace_back(block);
|
|
return true;
|
|
}
|
|
|
|
void StatusIter::reset() {
|
|
fStack.clear();
|
|
}
|