ultimatepp/uppsrc/ide/Browser/Base.cpp
cxl 3d9e13e4e4 .cpp
git-svn-id: svn://ultimatepp.org/upp/trunk@8445 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-05-13 14:01:22 +00:00

486 lines
12 KiB
C++

#include "Browser.h"
#include <plugin/lz4/lz4.h>
#define LTIMING(x) // RTIMING(x)
#define LLOG(x) // DLOG(x)
#define LTIMESTOP(x) // RTIMESTOP(x)
#define LDUMP(x) // DDUMP(x)
VectorMap<String, String> sSrcFile;
ArrayMap<String, SourceFileInfo> source_file;
void SourceFileInfo::Serialize(Stream& s)
{
s % time % ids % included_id_macros % namespace_info % using_info
% defined_macros % defined_namespace_info % includes;
}
String CodeBaseCacheDir()
{
return ConfigFile("cfg/codebase");
}
void ReduceCodeBaseCache()
{
struct FileInfo {
String path;
Time time;
int64 length;
bool operator<(const FileInfo& a) const { return time > a.time; }
};
Array<FileInfo> file;
FindFile ff(AppendFileName(CodeBaseCacheDir(), "*.*"));
int64 total = 0;
while(ff) {
if(ff.IsFile()) {
FileInfo& m = file.Add();
m.path = ff.GetPath();
m.time = ff.GetLastAccessTime();
m.length = ff.GetLength();
total += m.length;
}
ff.Next();
}
Sort(file);
while(total > 120000000 && file.GetCount()) {
DeleteFile(file.Top().path);
total -= file.Top().length;
file.Drop();
}
}
String CodeBaseCacheFile()
{
return AppendFileName(CodeBaseCacheDir(), GetVarsName() + '.' + GetCurrentMainPackage() + '.' + GetCurrentBuildMethod() + ".codebase");
}
CppBase& CodeBase()
{
static CppBase b;
return b;
}
static bool s_console;
void IdePutErrorLine(const String& line);
void BrowserScanError(int line, const String& text, int file)
{
if(s_console)
IdePutErrorLine(String().Cat() << source_file.GetKey(file) << " (" << line << "): " << text);
}
void SerializeCodeBase(Stream& s)
{
source_file.Serialize(s);
SerializePPFiles(s);
CodeBase().Serialize(s);
}
#define CPP_CODEBASE_VERSION 8
void SaveCodeBase()
{
LTIMING("SaveCodeBase");
LLOG("Save code base " << CodeBase().GetCount());
RealizeDirectory(ConfigFile("cfg/codebase"));
StringStream ss;
Store(callback(SerializeCodeBase), ss, CPP_CODEBASE_VERSION);
String data = ss.GetResult();
String path = CodeBaseCacheFile();
SaveFile(path, LZ4Compress(data));
}
bool TryLoadCodeBase(const char *pattern)
{
FindFile ff(pattern);
String path;
int64 len = -1;
while(ff) { // Load biggest file, as it has the most chances to have the data we need
if(ff.IsFile() && ff.GetLength() > len) {
path = ff.GetPath();
len = ff.GetLength();
}
ff.Next();
}
if(path.GetCount()) {
LTIMING("Load code base");
StringStream ss(LZ4Decompress(LoadFile(path)));
if(Load(callback(SerializeCodeBase), ss, CPP_CODEBASE_VERSION)) {
LLOG("Loaded " << ff.GetPath());
return true;
}
}
return false;
}
void LoadCodeBase()
{
TryLoadCodeBase(CodeBaseCacheFile()) ||
TryLoadCodeBase(AppendFileName(CodeBaseCacheDir(), GetVarsName() + ".*." + GetCurrentBuildMethod() + ".codebase")) ||
TryLoadCodeBase(AppendFileName(CodeBaseCacheDir(), GetVarsName() + ".*.codebase")) ||
TryLoadCodeBase(AppendFileName(CodeBaseCacheDir(), "*.codebase"));
LLOG("LoadCodeBase: " << CodeBase().GetCount());
}
void FinishCodeBase()
{
LTIMING("FinishBase");
Qualify(CodeBase());
}
void GatherSources(const String& master_path, const String& path_)
{
RHITCOUNT("GatherSources");
String path = NormalizePath(path_);
if(sSrcFile.Find(path) >= 0)
return;
sSrcFile.Add(path, master_path);
const PPFile& f = GetPPFile(path);
for(int i = 0; i < f.includes.GetCount(); i++) {
String p = GetIncludePath(f.includes[i], GetFileFolder(path));
if(p.GetCount())
GatherSources(master_path, p);
}
}
void BaseInfoSync(Progress& pi)
{ // clears temporary caches (file times etc..)
PPSync(TheIde()->IdeGetIncludePath());
LTIMESTOP("Gathering files");
sSrcFile.Clear();
const Workspace& wspc = GetIdeWorkspace();
LTIMING("Gathering files");
pi.SetText("Gathering files");
pi.SetTotal(wspc.GetCount());
for(int pass = 0; pass < 2; pass++)
for(int i = 0; i < wspc.GetCount(); i++) {
pi.Step();
const Package& pk = wspc.GetPackage(i);
String n = wspc[i];
for(int i = 0; i < pk.file.GetCount(); i++) {
String path = SourcePath(n, pk.file[i]);
if(pass ? IsHFile(path)
: IsCPPFile(path) || findarg(ToLower(GetFileExt(path)), ".lay", ".sch", ".iml") >= 0)
GatherSources(path, path);
}
}
SweepPPFiles(sSrcFile.GetIndex());
}
String GetMasterFile(const String& file)
{
return sSrcFile.Get(file, Null);
}
int GetSourceFileIndex(const String& path)
{
return source_file.FindPut(path);
}
String GetSourceFilePath(int file)
{
if(file < 0 || file >= source_file.GetCount())
return Null;
return source_file.GetKey(file);
}
bool CheckFile(const SourceFileInfo& f, const String& path)
{
RTIMING("CheckFile");
LDUMP(f.time);
LDUMP(FileGetTime(path));
if(f.time != FileGetTime(path))
return false;
Cpp pp;
FileIn in(path);
pp.Preprocess(path, in, GetMasterFile(path), true);
String included_id_macros = pp.GetIncludedMacroValues(f.ids.GetKeys());
LDUMP(included_id_macros);
LDUMP(f.included_id_macros);
return f.included_id_macros == included_id_macros;
}
void ParseFiles(Progress& pi, const Index<int>& parse_file)
{
pi.SetTotal(parse_file.GetCount());
pi.SetPos(0);
pi.AlignText(ALIGN_LEFT);
for(int i = 0; i < parse_file.GetCount(); i++) {
String path = GetSourceFilePath(parse_file[i]);
pi.SetText(GetFileName(GetFileFolder(path)) + "/" + GetFileName(path));
pi.Step();
FileIn fi(path);
LDUMP(path);
LDUMP(parse_file[i]);
bool dummy;
ParseSrc(fi, parse_file[i], callback1(BrowserScanError, i), true, false, dummy, dummy);
}
}
void UpdateCodeBase2(Progress& pi)
{
const Workspace& wspc = GetIdeWorkspace();
pi.SetText("Checking source files");
pi.SetTotal(sSrcFile.GetCount());
pi.SetPos(0);
Index<int> keep_file;
Index<int> parse_file;
for(int i = 0; i < sSrcFile.GetCount(); i++) {
pi.Step();
String path = sSrcFile.GetKey(i);
int q = GetSourceFileIndex(path);
const SourceFileInfo& f = source_file[q];
LLOG("== CHECK == " << q << ": " << path);
if(CheckFile(f, path))
keep_file.Add(q);
else {
LLOG("PARSE!");
parse_file.Add(q);
}
}
CppBase& base = CodeBase();
base.Sweep(keep_file);
for(int i = 0; i < source_file.GetCount(); i++)
if(keep_file.Find(i) < 0 && parse_file.Find(i) < 0 && !source_file.IsUnlinked(i))
source_file.Unlink(i);
ParseFiles(pi, parse_file);
}
void UpdateCodeBase(Progress& pi)
{
BaseInfoSync(pi);
UpdateCodeBase2(pi);
}
Vector<String> ParseSrc(Stream& in, int file, Callback2<int, const String&> error,
bool do_macros, bool get_changes,
bool& namespace_info_changed,
bool& includes_changed)
{
Vector<String> cm;
String path = GetSourceFilePath(file);
LLOG("====== Parse " << file << ": " << path);
Vector<String> pp;
String ext = ToLower(GetFileExt(path));
int filetype = FILE_OTHER;
SourceFileInfo& sfi = source_file[file];
Cpp cpp;
bool b = false;
if(ext == ".lay")
pp.Add(PreprocessLayFile(path));
else
if(ext == ".iml")
pp.Add(PreprocessImlFile(path));
else
if(ext == ".sch")
pp.Append(PreprocessSchFile(path));
else {
cpp.Preprocess(path, in, GetMasterFile(GetSourceFilePath(file)));
filetype = decode(ext, ".h", FILE_H, ".hpp", FILE_HPP,
".cpp",FILE_CPP, ".c", FILE_C, FILE_OTHER);
if(do_macros) {
sfi.ids = cpp.ids.PickKeys();
sfi.included_id_macros = cpp.GetIncludedMacroValues(sfi.ids.GetKeys());
LDUMP(sfi.ids);
sfi.time = FileGetTime(path);
LDUMP(sfi.time);
VectorMap<String, String> dm = cpp.GetDefinedMacros();
LDUMP(dm);
if(sfi.defined_macros != dm) {
LTIMING("Find changed macros");
if(get_changes) {
Buffer<bool> found(sfi.defined_macros.GetCount(), false);
for(int i = 0; i < dm.GetCount() && cm.GetCount() < 10; i++) {
String id = dm.GetKey(i);
int q = sfi.defined_macros.Find(id);
if(q < 0 || sfi.defined_macros[q] != dm[i])
cm.Add(id);
if(q >= 0)
found[q] = true;
}
for(int i = 0; i < sfi.defined_macros.GetCount() && cm.GetCount() < 10; i++)
if(!found[i])
cm.Add(sfi.defined_macros.GetKey(i));
}
sfi.defined_macros = pick(dm);
}
}
StringStream pin(cpp.output);
Parser p;
p.Do(pin, CodeBase(), file, filetype, GetFileName(path), error, Vector<String>(),
cpp.namespace_stack, cpp.namespace_using);
if(sfi.defined_namespace_info != p.namespace_info) {
sfi.defined_namespace_info = p.namespace_info;
namespace_info_changed = true;
}
if(cpp.includes != sfi.includes) {
sfi.includes = cpp.includes;
includes_changed = true;
}
return cm;
}
for(int i = 0; i < pp.GetCount(); i++) {
StringStream pin(pp[i]);
Parser p;
p.Do(pin, CodeBase(), file, filetype, GetFileName(path), error, Vector<String>(),
cpp.namespace_stack, cpp.namespace_using);
}
return cm;
}
bool HasIntersection(const Index<String>& ids, const Vector<String>& cm)
{
if(cm.GetCount() >= 10)
return true;
for(int i = 0; i < cm.GetCount(); i++)
if(ids.Find(cm[i]) >= 0)
return true;
return false;
}
void CodeBaseScanFile(Stream& in, const String& fn, bool check_macros)
{
LLOG("===== CodeBaseScanFile " << fn);
PPSync(TheIde()->IdeGetIncludePath());
LTIMING("CodeBaseScan");
int file = GetSourceFileIndex(fn);
CppBase& base = CodeBase();
base.RemoveFile(file);
SourceFileInfo& f = source_file[file];
bool namespace_info_changed = false;
bool includes_changed = false;
Vector<String> cm = ParseSrc(in, file, CNULL, check_macros, true,
namespace_info_changed, includes_changed);
LDUMP(f.defined_macros);
LDUMP(check_macros);
if(check_macros && (includes_changed || namespace_info_changed || cm.GetCount())) {
SyncCodeBase();
return;
}
/*
if( && check_macros) {
}
if((namespace_info_changed || cm.GetCount()) && check_macros) {
SyncCodeBase(); return; _DBG_
DLOG("Checking files");
Progress pi;
BaseInfoSync(pi);
pi.SetText("Checking source files");
pi.SetTotal(sSrcFile.GetCount());
pi.SetPos(0);
Index<int> parse_file;
for(int i = 0; i < sSrcFile.GetCount(); i++) {
pi.Step();
String path = sSrcFile.GetKey(i);
DDUMP(path);
int q = GetSourceFileIndex(path);
const SourceFileInfo& f = source_file[q];
if((namespace_info_changed || HasIntersection(f.ids, cm)) && !CheckFile(f, path))
parse_file.Add(q);
}
CodeBase().RemoveFiles(parse_file);
ParseFiles(pi, parse_file);
}
*/
FinishCodeBase();
}
void CodeBaseScanFile(const String& fn, bool check_macros)
{
FileIn in(fn);
CodeBaseScanFile(in, fn, check_macros);
}
void ClearCodeBase()
{
CleanPP();
CodeBase().Clear();
source_file.Clear();
}
void SyncCodeBase()
{
LTIMING("SyncCodeBase");
LTIMESTOP("SyncCodeBase");
Progress pi;
pi.Title("Parsing source files");
UpdateCodeBase(pi);
FinishCodeBase();
}
void NewCodeBase()
{
ReduceCodeBaseCache();
static int start;
if(start) return;
start++;
LoadCodeBase();
LOG("NewCodeBase loaded " << CodeBase().GetCount());
SyncCodeBase();
LOG("NewCodeBase synced " << CodeBase().GetCount());
SaveCodeBase();
LOG("NewCodeBase saved " << CodeBase().GetCount());
start--;
}
/*
void CheckCodeBase()
{
RTIMESTOP("CheckCodeBase");
Progress pi;
pi.Title("Checking source files");
BaseInfoSync(pi);
for(int i = 0; i < sSrcFile.GetCount(); i++)
if(source_file.Find(sSrcFile.GetKey(i)) < 0) {
UpdateCodeBase2(pi);
FinishCodeBase();
return;
}
for(int i = 0; i < source_file.GetCount(); i++)
if(!source_file.IsUnlinked(i)) {
String path = source_file.GetKey(i);
if(sSrcFile.Find(source_file.GetKey(i)) < 0 || source_file[i].time != FileGetTime(path)) {
UpdateCodeBase2(pi);
FinishCodeBase();
return;
}
}
}
*/
void RescanCodeBase()
{
ClearCodeBase();
s_console = true;
Progress pi;
pi.Title("Parsing source files");
UpdateCodeBase(pi);
FinishCodeBase();
s_console = false;
}
bool ExistsBrowserItem(const String& item)
{
return GetCodeRefItem(item);
}