186d08f621
- More rigorous symbol identification creates additional links, and finds spurious errors like unnecessary parentheses. - Fixed code to allow preprocessor directives in #Code blocks (still more to do). - Fixed examples waiting on fiddle updates. - Make SkImageInfo officially documented (update in status.json). - Fixed enum indentation. Docs-Preview: https://skia.org/?cl=115656 Bug: skia:6898 Change-Id: Ifcf5cfd3f6c03b3b83155c898b2b035a2f3d443c Reviewed-on: https://skia-review.googlesource.com/115656 Commit-Queue: Cary Clark <caryclark@skia.org> Reviewed-by: Cary Clark <caryclark@skia.org>
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, OneFile oneFile) {
|
|
if (!sk_isdir(fileOrPath)) {
|
|
if (!this->parseFromFile(fileOrPath)) {
|
|
SkDebugf("failed to parse %s\n", fileOrPath);
|
|
return false;
|
|
}
|
|
} else if (OneFile::kNo == oneFile) {
|
|
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();
|
|
}
|