Speed up build

This commit is contained in:
Fangrui Song 2018-02-23 18:24:54 -08:00
parent c68548a2ca
commit bee8bacefd
5 changed files with 214 additions and 222 deletions

View File

@ -5,6 +5,7 @@
#include "optional.h" #include "optional.h"
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
struct FileContents { struct FileContents {

View File

@ -30,6 +30,15 @@ void VerifyUnique(const std::vector<T>& values0) {
#endif #endif
} }
template <typename T>
void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
dest->erase(
std::remove_if(dest->begin(), dest->end(),
[&](const T& t) { return to_remove_set.count(t) > 0; }),
dest->end());
}
optional<QueryType::Def> ToQuery(const IdMap& id_map, optional<QueryType::Def> ToQuery(const IdMap& id_map,
const IndexType::Def& type) { const IndexType::Def& type) {
if (type.detailed_name.empty()) if (type.detailed_name.empty())

View File

@ -5,8 +5,12 @@
#include "serializer.h" #include "serializer.h"
#include "utils.h" #include "utils.h"
#include <doctest/doctest.h>
#include <loguru/loguru.hpp>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <fstream>
#include <iostream> #include <iostream>
// The 'diff' utility is available and we can use dprintf(3). // The 'diff' utility is available and we can use dprintf(3).
@ -33,6 +37,155 @@ std::string ToString(const rapidjson::Document& document) {
return UpdateToRnNewlines(output); return UpdateToRnNewlines(output);
} }
void ParseTestExpectation(
const std::string& filename,
const std::vector<std::string>& lines_with_endings,
TextReplacer* replacer,
std::vector<std::string>* flags,
std::unordered_map<std::string, std::string>* output_sections) {
#if false
#include "bar.h"
void foo();
/*
// DOCS for TEXT_REPLACE:
// Each line under TEXT_REPLACE is a replacement, ie, the two entries will be
// considered equivalent. This is useful for USRs which vary across files.
// DOCS for EXTRA_FLAGS:
// Additional flags to pass to clang.
// DOCS for OUTPUT:
// If no name is given assume to be this file name. If there is not an output
// section for a file it is not checked.
TEXT_REPLACE:
foo <===> bar
one <===> two
EXTRA_FLAGS:
-std=c++14
OUTPUT:
{}
OUTPUT: bar.cc
{}
OUTPUT: bar.h
{}
*/
#endif
// Scan for TEXT_REPLACE:
{
bool in_output = false;
for (std::string line : lines_with_endings) {
TrimInPlace(line);
if (StartsWith(line, "TEXT_REPLACE:")) {
assert(!in_output && "multiple TEXT_REPLACE sections");
in_output = true;
continue;
}
if (in_output && line.empty())
break;
if (in_output) {
static const std::string kKey = " <===> ";
size_t index = line.find(kKey);
LOG_IF_S(FATAL, index == std::string::npos)
<< " No '" << kKey << "' in replacement string '" << line << "'"
<< ", index=" << index;
TextReplacer::Replacement replacement;
replacement.from = line.substr(0, index);
replacement.to = line.substr(index + kKey.size());
TrimInPlace(replacement.from);
TrimInPlace(replacement.to);
replacer->replacements.push_back(replacement);
}
}
}
// Scan for EXTRA_FLAGS:
{
bool in_output = false;
for (std::string line : lines_with_endings) {
TrimInPlace(line);
if (StartsWith(line, "EXTRA_FLAGS:")) {
assert(!in_output && "multiple EXTRA_FLAGS sections");
in_output = true;
continue;
}
if (in_output && line.empty())
break;
if (in_output)
flags->push_back(line);
}
}
// Scan for OUTPUT:
{
std::string active_output_filename;
std::string active_output_contents;
bool in_output = false;
for (std::string line_with_ending : lines_with_endings) {
if (StartsWith(line_with_ending, "*/"))
break;
if (StartsWith(line_with_ending, "OUTPUT:")) {
// Terminate the previous output section if we found a new one.
if (in_output) {
(*output_sections)[active_output_filename] = active_output_contents;
}
// Try to tokenize OUTPUT: based one whitespace. If there is more than
// one token assume it is a filename.
std::vector<std::string> tokens = SplitString(line_with_ending, " ");
if (tokens.size() > 1) {
active_output_filename = tokens[1];
TrimInPlace(active_output_filename);
} else {
active_output_filename = filename;
}
active_output_contents = "";
in_output = true;
} else if (in_output)
active_output_contents += line_with_ending;
}
if (in_output)
(*output_sections)[active_output_filename] = active_output_contents;
}
}
void UpdateTestExpectation(const std::string& filename,
const std::string& expectation,
const std::string& actual) {
// Read the entire file into a string.
std::ifstream in(filename);
std::string str;
str.assign(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
in.close();
// Replace expectation
auto it = str.find(expectation);
assert(it != std::string::npos);
str.replace(it, expectation.size(), actual);
// Write it back out.
WriteToFile(filename, str);
}
void DiffDocuments(std::string path, void DiffDocuments(std::string path,
std::string path_section, std::string path_section,
rapidjson::Document& expected, rapidjson::Document& expected,
@ -309,3 +462,54 @@ bool RunIndexTests(const std::string& filter_path, bool enable_update) {
// TODO: ctor/dtor, copy ctor // TODO: ctor/dtor, copy ctor
// TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile& // TODO: Always pass IndexFile by pointer, ie, search and remove all IndexFile&
// refs. // refs.
TEST_SUITE("ParseTestExpectation") {
TEST_CASE("Parse TEXT_REPLACE") {
// clang-format off
std::vector<std::string> lines_with_endings = {
"/*\n",
"TEXT_REPLACE:\n",
" foo <===> \tbar \n",
"01 <===> 2\n",
"\n",
"*/\n"};
// clang-format on
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
&all_expected_output);
REQUIRE(text_replacer.replacements.size() == 2);
REQUIRE(text_replacer.replacements[0].from == "foo");
REQUIRE(text_replacer.replacements[0].to == "bar");
REQUIRE(text_replacer.replacements[1].from == "01");
REQUIRE(text_replacer.replacements[1].to == "2");
}
TEST_CASE("Apply TEXT_REPLACE") {
TextReplacer replacer;
replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
// Equal-length.
REQUIRE(replacer.Apply("foo") == "bar");
REQUIRE(replacer.Apply("bar") == "bar");
// Shorter replacement.
REQUIRE(replacer.Apply("01") == "2");
REQUIRE(replacer.Apply("2") == "2");
// Longer replacement.
REQUIRE(replacer.Apply("3") == "456");
REQUIRE(replacer.Apply("456") == "456");
// Content before-after replacement.
REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
// Multiple replacements.
REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
}
}

View File

@ -374,155 +374,6 @@ std::string TextReplacer::Apply(const std::string& content) {
return result; return result;
} }
void ParseTestExpectation(
const std::string& filename,
const std::vector<std::string>& lines_with_endings,
TextReplacer* replacer,
std::vector<std::string>* flags,
std::unordered_map<std::string, std::string>* output_sections) {
#if false
#include "bar.h"
void foo();
/*
// DOCS for TEXT_REPLACE:
// Each line under TEXT_REPLACE is a replacement, ie, the two entries will be
// considered equivalent. This is useful for USRs which vary across files.
// DOCS for EXTRA_FLAGS:
// Additional flags to pass to clang.
// DOCS for OUTPUT:
// If no name is given assume to be this file name. If there is not an output
// section for a file it is not checked.
TEXT_REPLACE:
foo <===> bar
one <===> two
EXTRA_FLAGS:
-std=c++14
OUTPUT:
{}
OUTPUT: bar.cc
{}
OUTPUT: bar.h
{}
*/
#endif
// Scan for TEXT_REPLACE:
{
bool in_output = false;
for (std::string line : lines_with_endings) {
TrimInPlace(line);
if (StartsWith(line, "TEXT_REPLACE:")) {
assert(!in_output && "multiple TEXT_REPLACE sections");
in_output = true;
continue;
}
if (in_output && line.empty())
break;
if (in_output) {
static const std::string kKey = " <===> ";
size_t index = line.find(kKey);
LOG_IF_S(FATAL, index == std::string::npos)
<< " No '" << kKey << "' in replacement string '" << line << "'"
<< ", index=" << index;
TextReplacer::Replacement replacement;
replacement.from = line.substr(0, index);
replacement.to = line.substr(index + kKey.size());
TrimInPlace(replacement.from);
TrimInPlace(replacement.to);
replacer->replacements.push_back(replacement);
}
}
}
// Scan for EXTRA_FLAGS:
{
bool in_output = false;
for (std::string line : lines_with_endings) {
TrimInPlace(line);
if (StartsWith(line, "EXTRA_FLAGS:")) {
assert(!in_output && "multiple EXTRA_FLAGS sections");
in_output = true;
continue;
}
if (in_output && line.empty())
break;
if (in_output)
flags->push_back(line);
}
}
// Scan for OUTPUT:
{
std::string active_output_filename;
std::string active_output_contents;
bool in_output = false;
for (std::string line_with_ending : lines_with_endings) {
if (StartsWith(line_with_ending, "*/"))
break;
if (StartsWith(line_with_ending, "OUTPUT:")) {
// Terminate the previous output section if we found a new one.
if (in_output) {
(*output_sections)[active_output_filename] = active_output_contents;
}
// Try to tokenize OUTPUT: based one whitespace. If there is more than
// one token assume it is a filename.
std::vector<std::string> tokens = SplitString(line_with_ending, " ");
if (tokens.size() > 1) {
active_output_filename = tokens[1];
TrimInPlace(active_output_filename);
} else {
active_output_filename = filename;
}
active_output_contents = "";
in_output = true;
} else if (in_output)
active_output_contents += line_with_ending;
}
if (in_output)
(*output_sections)[active_output_filename] = active_output_contents;
}
}
void UpdateTestExpectation(const std::string& filename,
const std::string& expectation,
const std::string& actual) {
// Read the entire file into a string.
std::ifstream in(filename);
std::string str;
str.assign(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
in.close();
// Replace expectation
auto it = str.find(expectation);
assert(it != std::string::npos);
str.replace(it, expectation.size(), actual);
// Write it back out.
WriteToFile(filename, str);
}
void WriteToFile(const std::string& filename, const std::string& content) { void WriteToFile(const std::string& filename, const std::string& content) {
std::ofstream file(filename, std::ofstream file(filename,
std::ios::out | std::ios::trunc | std::ios::binary); std::ios::out | std::ios::trunc | std::ios::binary);
@ -601,57 +452,6 @@ std::string UpdateToRnNewlines(std::string output) {
return output; return output;
}; };
TEST_SUITE("ParseTestExpectation") {
TEST_CASE("Parse TEXT_REPLACE") {
// clang-format off
std::vector<std::string> lines_with_endings = {
"/*\n",
"TEXT_REPLACE:\n",
" foo <===> \tbar \n",
"01 <===> 2\n",
"\n",
"*/\n"};
// clang-format on
TextReplacer text_replacer;
std::vector<std::string> flags;
std::unordered_map<std::string, std::string> all_expected_output;
ParseTestExpectation("foo.cc", lines_with_endings, &text_replacer, &flags,
&all_expected_output);
REQUIRE(text_replacer.replacements.size() == 2);
REQUIRE(text_replacer.replacements[0].from == "foo");
REQUIRE(text_replacer.replacements[0].to == "bar");
REQUIRE(text_replacer.replacements[1].from == "01");
REQUIRE(text_replacer.replacements[1].to == "2");
}
TEST_CASE("Apply TEXT_REPLACE") {
TextReplacer replacer;
replacer.replacements.push_back(TextReplacer::Replacement{"foo", "bar"});
replacer.replacements.push_back(TextReplacer::Replacement{"01", "2"});
replacer.replacements.push_back(TextReplacer::Replacement{"3", "456"});
// Equal-length.
REQUIRE(replacer.Apply("foo") == "bar");
REQUIRE(replacer.Apply("bar") == "bar");
// Shorter replacement.
REQUIRE(replacer.Apply("01") == "2");
REQUIRE(replacer.Apply("2") == "2");
// Longer replacement.
REQUIRE(replacer.Apply("3") == "456");
REQUIRE(replacer.Apply("456") == "456");
// Content before-after replacement.
REQUIRE(replacer.Apply("aaaa01bbbb") == "aaaa2bbbb");
// Multiple replacements.
REQUIRE(replacer.Apply("foofoobar0123") == "barbarbar22456");
}
}
TEST_SUITE("Update \\n to \\r\\n") { TEST_SUITE("Update \\n to \\r\\n") {
TEST_CASE("all") { TEST_CASE("all") {
REQUIRE(UpdateToRnNewlines("\n") == "\r\n"); REQUIRE(UpdateToRnNewlines("\n") == "\r\n");

View File

@ -7,8 +7,6 @@
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
// Trim from start (in place) // Trim from start (in place)
@ -111,17 +109,6 @@ struct TextReplacer {
std::string Apply(const std::string& content); std::string Apply(const std::string& content);
}; };
void ParseTestExpectation(
const std::string& filename,
const std::vector<std::string>& lines_with_endings,
TextReplacer* text_replacer,
std::vector<std::string>* flags,
std::unordered_map<std::string, std::string>* output_sections);
void UpdateTestExpectation(const std::string& filename,
const std::string& expectation,
const std::string& actual);
void WriteToFile(const std::string& filename, const std::string& content); void WriteToFile(const std::string& filename, const std::string& content);
// note: this implementation does not disable this overload for array types // note: this implementation does not disable this overload for array types
@ -143,15 +130,6 @@ void AddRange(std::vector<T>* dest, std::vector<T>&& to_add) {
std::make_move_iterator(to_add.end())); std::make_move_iterator(to_add.end()));
} }
template <typename T>
void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
dest->erase(
std::remove_if(dest->begin(), dest->end(),
[&](const T& t) { return to_remove_set.count(t) > 0; }),
dest->end());
}
// http://stackoverflow.com/a/38140932 // http://stackoverflow.com/a/38140932
// //
// struct SomeHashKey { // struct SomeHashKey {