mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
867 lines
25 KiB
C++
867 lines
25 KiB
C++
#include "ide.h"
|
|
|
|
#include <plugin/bz2/bz2.h>
|
|
|
|
#define LDUMP(x) // DUMP(x)
|
|
|
|
const TargetMode& Ide::GetTargetMode()
|
|
{
|
|
return (targetmode == 0 ? debug : release);
|
|
}
|
|
|
|
Index<String> Ide::PackageConfig(const Workspace& wspc, int package,
|
|
const VectorMap<String, String>& bm, String mainparam,
|
|
Host& host, Builder& b, String *target)
|
|
{
|
|
String packagepath = PackagePath(wspc[package]);
|
|
const Package& pkg = wspc.package[package];
|
|
Index<String> cfg;
|
|
mainparam << ' ' << bm.Get(targetmode ? "RELEASE_FLAGS" : "DEBUG_FLAGS", NULL);
|
|
cfg = SplitFlags(mainparam, package == 0, wspc.GetAllAccepts(package));
|
|
cfg.FindAdd(bm.Get("BUILDER", "GCC"));
|
|
const TargetMode& m = GetTargetMode();
|
|
if(targetmode == 0)
|
|
cfg.FindAdd("DEBUG");
|
|
switch(m.linkmode) {
|
|
case 2:
|
|
cfg.FindAdd("SO");
|
|
case 1:
|
|
cfg.FindAdd("SHARED");
|
|
}
|
|
if(targetmode == 2)
|
|
cfg.FindAdd("FORCE_SPEED");
|
|
if(targetmode == 3)
|
|
cfg.FindAdd("FORCE_SIZE");
|
|
int q = m.package.Find(wspc[package]);
|
|
if(q >= 0) {
|
|
const PackageMode& p = m.package[q];
|
|
switch(p.debug >= 0 ? p.debug : m.def.debug) {
|
|
case 1: cfg.FindAdd("DEBUG_MINIMAL"); break;
|
|
case 2: cfg.FindAdd("DEBUG_FULL"); break;
|
|
}
|
|
if(!pkg.noblitz && (p.blitz >= 0 ? p.blitz : m.def.blitz))
|
|
cfg.FindAdd("BLITZ");
|
|
}
|
|
else {
|
|
switch(m.def.debug) {
|
|
case 1: cfg.FindAdd("DEBUG_MINIMAL"); break;
|
|
case 2: cfg.FindAdd("DEBUG_FULL"); break;
|
|
}
|
|
if(!pkg.noblitz && m.def.blitz)
|
|
cfg.FindAdd("BLITZ");
|
|
}
|
|
host.AddFlags(cfg);
|
|
b.AddFlags(cfg);
|
|
if(target)
|
|
*target = Gather(pkg.target, cfg.GetKeys(), true);
|
|
return cfg;
|
|
}
|
|
|
|
static String NoCr(const char *s)
|
|
{
|
|
String out;
|
|
while(*s)
|
|
{
|
|
const char *b = s;
|
|
while(*s && *s != '\r')
|
|
s++;
|
|
out.Cat(b, int(s - b));
|
|
if(*s == '\r')
|
|
s++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
bool Ide::SyncHostFiles(RemoteHost& host)
|
|
{
|
|
HdependTimeDirty();
|
|
String query = "<";
|
|
// Vector<String> remotepath;
|
|
Vector<String> fdata;
|
|
ArrayMap<String, TransferFileInfo> info;
|
|
Vector<int> nocrsize;
|
|
const Workspace& wspc = IdeWorkspace();
|
|
int p;
|
|
for(p = 0; p < wspc.GetCount(); p++) {
|
|
const Package& pkg = wspc.GetPackage(p);
|
|
String pn = wspc[p];
|
|
for(int f = -1; f < pkg.file.GetCount(); f++)
|
|
if(f < 0 || !pkg.file[f].separator) {
|
|
Vector<String> pkgfiles;
|
|
String pk = (f >= 0 ? SourcePath(pn, pkg.file[f]) : PackagePath(pn));
|
|
if(!FindFile(pk).IsFile())
|
|
continue;
|
|
pkgfiles.Add(f >= 0 ? SourcePath(pn, pkg.file[f]) : PackagePath(pn));
|
|
pkgfiles.AppendPick(HdependGetDependencies(pkgfiles[0]));
|
|
for(int d = 0; d < pkgfiles.GetCount(); d++) {
|
|
TransferFileInfo newinfo;
|
|
newinfo.sourcepath = pkgfiles[d];
|
|
String rp = host.GetHostPath(newinfo.sourcepath);
|
|
if(info.Find(rp) >= 0 || (d && UnixPath(rp) == UnixPath(newinfo.sourcepath)))
|
|
continue;
|
|
FindFile ff(newinfo.sourcepath);
|
|
newinfo.filetime = (int)(Time(ff.GetLastWriteTime()) - RemoteHost::TimeBase());
|
|
newinfo.filesize = (int)ff.GetLength();
|
|
int pos = transferfilecache.Find(rp);
|
|
if(pos < 0 || transferfilecache[pos].filetime != newinfo.filetime
|
|
|| transferfilecache[pos].filesize != newinfo.filesize)
|
|
{
|
|
String content = NoCr(LoadFile(newinfo.sourcepath));
|
|
query << rp << '\t' << newinfo.filetime << '\t' << content.GetLength() << '\n';
|
|
// remotepath.Add(rp);
|
|
fdata.Add(content);
|
|
info.Add(rp, newinfo);
|
|
nocrsize.Add(content.GetLength());
|
|
PutVerbose("Checking remote " + rp);
|
|
/*
|
|
if(msecs(ticks) >= 0)
|
|
{
|
|
ShowConsole();
|
|
console.Sync();
|
|
ticks = msecs(-200);
|
|
}
|
|
*/ }
|
|
}
|
|
}
|
|
}
|
|
if(info.IsEmpty())
|
|
return true;
|
|
// String host = GetVar("REMOTE_HOST");
|
|
// int port = 2346;
|
|
// int ppos = host.Find(':');
|
|
// if(ppos >= 0)
|
|
// {
|
|
// port = ScanInt(host.GetIter(ppos + 1));
|
|
// host.Trim(ppos);
|
|
// }
|
|
|
|
Index<String> ignore;
|
|
{
|
|
console << NFormat("Retrieving update list for remote filesystem %s\n", host.host);
|
|
ShowConsole();
|
|
console.Sync();
|
|
String out = host.RemoteExec(query);
|
|
if(out[0] != 'O' || out[1] != 'K')
|
|
{
|
|
console << out << "\n";
|
|
return false;
|
|
}
|
|
|
|
const char *s = out;
|
|
while(*s && *s++ != '\n')
|
|
;
|
|
while(*s)
|
|
{
|
|
const char *b = s;
|
|
while(*s && *s != '\n')
|
|
s++;
|
|
ignore.FindAdd(NormalizePath(NativePath(String(b, s))));
|
|
if(*s)
|
|
s++;
|
|
}
|
|
if(ignore.GetCount() == info.GetCount())
|
|
console << "Remote source tree is up to date.\n";
|
|
else
|
|
{
|
|
console << NFormat("%d file(s) missing in remote source tree:\n", info.GetCount() - ignore.GetCount());
|
|
for(p = 0; p < info.GetCount(); p++)
|
|
{
|
|
// console << " " << remotepath[p] << " - "
|
|
// << FormatInt(p) << " @ " << FormatInt(ignore.Find(remotepath[p])) << "\n";
|
|
if(ignore.Find(NormalizePath(NativePath(info.GetKey(p)))) < 0)
|
|
console << " " << info.GetKey(p) << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector<String> update;
|
|
for(p = 0; p < info.GetCount(); p++)
|
|
if(ignore.Find(NormalizePath(NativePath(info.GetKey(p)))) < 0)
|
|
{
|
|
String rawdata = fdata[p];
|
|
console << "Compressing " << info[p].sourcepath << " (" << FormatInt(rawdata.GetLength()) << " B)\n";
|
|
console.Sync();
|
|
String bzdata = BZ2Compress(rawdata);
|
|
// console << ", BZ2 = " << FormatInt(bzdata.GetLength()) << " B";
|
|
// console.Sync();
|
|
String comp = ASCII85Encode(bzdata);
|
|
// console << ", ASCII85 = " << FormatInt(comp.GetLength()) << "\n";
|
|
// console.Sync();
|
|
if(update.IsEmpty() || update.Top().GetLength() + comp.GetLength() >= 500000)
|
|
update.Add(">");
|
|
update.Top() << info.GetKey(p) << '\t' << info[p].filetime << '\t' << nocrsize[p] << '\t' << comp << '\n';
|
|
}
|
|
|
|
for(p = 0; p < update.GetCount(); p++)
|
|
{
|
|
console << NFormat("Uploading block %d / %d (%d B)\n", p + 1, update.GetCount(), update[p].GetLength());
|
|
ShowConsole();
|
|
console.Sync();
|
|
String result = host.RemoteExec(update[p]);
|
|
if(result[0] != 'O' || result[1] != 'K')
|
|
{
|
|
console << result << "\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for(p = 0; p < info.GetCount(); p++)
|
|
transferfilecache.GetAdd(info.GetKey(p)) = info[p];
|
|
return true;
|
|
}
|
|
|
|
One<Host> Ide::CreateHost(bool sync_files)
|
|
{
|
|
SetupDefaultMethod();
|
|
VectorMap<String, String> bm = GetMethodVars(method);
|
|
String rm = bm.Get("REMOTE_HOST", Null);
|
|
One<Host> outhost;
|
|
if(!IsNull(rm)) {
|
|
One<RemoteHost> host = new RemoteHost;
|
|
host->host = rm;
|
|
host->port = 2346;
|
|
int f = rm.Find(':');
|
|
if(f >= 0) {
|
|
host->host = rm.Left(f);
|
|
host->port = Nvl(ScanInt(rm.GetIter(f + 1)), host->port);
|
|
}
|
|
host->os_type = bm.Get("REMOTE_OS", "UNIX");
|
|
Vector<String> path_map = Split(bm.Get("REMOTE_MAP", Null), ';');
|
|
for(int p = 0; p < path_map.GetCount(); p++) {
|
|
f = path_map[p].Find('>');
|
|
if(f >= 0) {
|
|
host->path_map_local.Add(path_map[p].Left(f));
|
|
host->path_map_remote.Add(path_map[p].Mid(f + 1));
|
|
}
|
|
}
|
|
VectorMap<String, String> env(Environment(), 1);
|
|
Vector<String> exedirs = SplitDirs(bm.Get("PATH", "") + ';' + env.Get("PATH", ""));
|
|
env.GetAdd("PATH") = Join(exedirs, ";");
|
|
for(int i = 0; i < env.GetCount(); i++)
|
|
host->environment << env.GetKey(i) << '=' << env[i] << '\0';
|
|
host->environment.Cat(0);
|
|
if(sync_files && bm.Get("REMOTE_TRANSFER", Null) != "0")
|
|
SyncHostFiles(*host);
|
|
outhost = -host;
|
|
}
|
|
else {
|
|
One<LocalHost> host = new LocalHost;
|
|
VectorMap<String, String> env(Environment(), 1);
|
|
host->exedirs = SplitDirs(bm.Get("PATH", "") + ';' + env.Get("PATH", ""));
|
|
env.GetAdd("PATH") = Join(host->exedirs, ";");
|
|
env.GetAdd("UPP_MAIN__") = GetFileDirectory(PackagePath(main));
|
|
for(int i = 0; i < env.GetCount(); i++) {
|
|
LDUMP(env.GetKey(i));
|
|
LDUMP(env[i]);
|
|
host->environment << env.GetKey(i) << '=' << env[i] << '\0';
|
|
}
|
|
host->environment.Cat(0);
|
|
host->cmdout = &cmdout;
|
|
outhost = -host;
|
|
}
|
|
return outhost;
|
|
}
|
|
|
|
One<Builder> Ide::CreateBuilder(Host *host)
|
|
{
|
|
SetupDefaultMethod();
|
|
VectorMap<String, String> bm = GetMethodVars(method);
|
|
String builder = bm.Get("BUILDER", "GCC");
|
|
One<Builder> b = (*BuilderMap().Get(builder))();
|
|
b->host = host;
|
|
b->compiler = bm.Get("COMPILER", "");
|
|
b->include = SplitDirs(GetVar("UPP") + ';' + bm.Get("INCLUDE", ""));
|
|
b->libpath = SplitDirs(bm.Get("LIB", ""));
|
|
b->debug_options = bm.Get("DEBUG_OPTIONS", "");
|
|
b->release_options = bm.Get("RELEASE_OPTIONS", "");
|
|
b->release_size_options = bm.Get("RELEASE_SIZE_OPTIONS", "");
|
|
b->script = bm.Get("SCRIPT", "");
|
|
return b;
|
|
}
|
|
|
|
int CharFilterSlash(int c)
|
|
{
|
|
return c == '\\' ? '/' : c;
|
|
}
|
|
|
|
String Ide::OutDir(const Index<String>& cfg, const String& package, const VectorMap<String, String>& bm,
|
|
bool use_target)
|
|
{
|
|
Index<String> excl;
|
|
excl.Add(bm.Get("BUILDER", "GCC"));
|
|
excl.Add("MSC");
|
|
LocalHost().AddFlags(excl);
|
|
Vector<String> x;
|
|
bool dbg = cfg.Find("DEBUG_FULL") >= 0 || cfg.Find("DEBUG_MINIMAL") >= 0;
|
|
if(cfg.Find("DEBUG") >= 0) {
|
|
excl.Add("BLITZ");
|
|
if(cfg.Find("BLITZ") < 0)
|
|
x.Add("NOBLITZ");
|
|
}
|
|
else
|
|
if(dbg)
|
|
x.Add("RELEASE");
|
|
if(use_target)
|
|
excl.Add("MAIN");
|
|
for(int i = 0; i < cfg.GetCount(); i++)
|
|
if(excl.Find(cfg[i]) < 0)
|
|
x.Add(cfg[i]);
|
|
Sort(x);
|
|
for(int i = 0; i < x.GetCount(); i++)
|
|
x[i] = InitCaps(x[i]);
|
|
String outdir = GetVar("OUTPUT");
|
|
if(!use_target)
|
|
outdir = AppendFileName(outdir, package);
|
|
outdir = AppendFileName(outdir, method + "." + Join(x, "."));
|
|
outdir = Filter(outdir, CharFilterSlash);
|
|
return outdir;
|
|
}
|
|
|
|
struct OneFileHost : Host {
|
|
One<Host> host;
|
|
String onefile;
|
|
|
|
virtual String GetEnvironment() { return host->GetEnvironment(); }
|
|
virtual String GetHostPath(const String& path) { return host->GetHostPath(path); }
|
|
virtual String GetLocalPath(const String& path) { return host->GetLocalPath(path); }
|
|
virtual String NormalizePath(const String& path) { return host->NormalizePath(path); }
|
|
virtual void DeleteFile(const Vector<String>& path) { host->DeleteFile(path); }
|
|
virtual void DeleteFolderDeep(const String& folder) { host->DeleteFolderDeep(folder); }
|
|
virtual void ChDir(const String& path) { host->ChDir(path); }
|
|
virtual void RealizeDir(const String& path) { host->RealizeDir(path); }
|
|
virtual void SaveFile(const String& path, const String& data) { host->SaveFile(path, data); }
|
|
virtual String LoadFile(const String& path) { return host->LoadFile(path); }
|
|
virtual int Execute(const char *c) { return host->Execute(c); }
|
|
virtual int ExecuteWithInput(const char *c) { return host->ExecuteWithInput(c); }
|
|
virtual int Execute(const char *c, Stream& o) { return host->Execute(c, o); }
|
|
virtual int AllocSlot() { return host->AllocSlot(); }
|
|
virtual bool Run(const char *cmdline, int slot, String key, int blitz_count) { return host->Run(cmdline, slot, key, blitz_count); }
|
|
virtual bool Run(const char *cmdline, Stream& out, int slot, String key, int blitz_count) { return host->Run(cmdline, out, slot, key, blitz_count); }
|
|
virtual bool Wait() { return host->Wait(); }
|
|
virtual One<SlaveProcess> StartProcess(const char *c) { return host->StartProcess(c); }
|
|
virtual void Launch(const char *cmdline, bool) { host->Launch(cmdline); }
|
|
virtual void AddFlags(Index<String>& cfg) { host->AddFlags(cfg); }
|
|
|
|
virtual Vector<FileInfo> GetFileInfo(const Vector<String>& path) {
|
|
Vector<FileInfo> fi = host->GetFileInfo(path);
|
|
for(int i = 0; i < path.GetCount(); i++)
|
|
if(path[i] == onefile)
|
|
(Time &)fi[i] = GetSysTime();
|
|
else
|
|
(Time &)fi[i] = Time::Low();
|
|
return fi;
|
|
}
|
|
};
|
|
|
|
bool Ide::BuildPackage(const Workspace& wspc, int pkindex, int pknumber, int pkcount,
|
|
String mainparam, String outfile, Vector<String>& linkfile, String& linkopt, bool link)
|
|
{
|
|
String package = wspc[pkindex];
|
|
String mainpackage = wspc[0];
|
|
const Package& pkg = wspc.package[pkindex];
|
|
VectorMap<String, String> bm = GetMethodVars(method);
|
|
if(bm.GetCount() == 0) {
|
|
PutConsole("Invalid build method");
|
|
return false;
|
|
}
|
|
One<Host> host = CreateHost(false);
|
|
if(!IsNull(onefile)) {
|
|
OneFileHost *h = new OneFileHost;
|
|
h->host = host;
|
|
h->onefile = onefile;
|
|
host = h;
|
|
}
|
|
One<Builder> b = CreateBuilder(~host);
|
|
b->config = PackageConfig(wspc, pkindex, bm, mainparam, *host, *b);
|
|
const TargetMode& m = targetmode == 0 ? debug : release;
|
|
b->version = m.version;
|
|
b->method = method;
|
|
b->outdir = OutDir(b->config, package, bm);
|
|
host->RealizeDir(b->outdir);
|
|
String mainfn = Null;
|
|
Index<String> mcfg = PackageConfig(wspc, 0, bm, mainparam, *host, *b, &mainfn);
|
|
HdependClearDependencies();
|
|
for(int i = 0; i < pkg.GetCount(); i++) {
|
|
const Array<OptItem>& f = pkg[i].depends;
|
|
for(int j = 0; j < f.GetCount(); j++)
|
|
if(MatchWhen(f[j].when, mcfg.GetKeys()))
|
|
HdependAddDependency(SourcePath(package, pkg[i]), SourcePath(package, f[j].text));
|
|
}
|
|
String tout = OutDir(mcfg, mainpackage, bm, use_target);
|
|
host->RealizeDir(tout);
|
|
if(IsNull(mainfn))
|
|
mainfn = GetFileTitle(mainpackage) + b->GetTargetExt();
|
|
if(!IsNull(outfile))
|
|
target = NormalizePath(outfile, tout);
|
|
else {
|
|
if(m.target_override && !IsNull(m.target) && IsFolder(m.target))
|
|
target = host->NormalizePath(AppendFileName(m.target, mainfn));
|
|
else
|
|
if(m.target_override && (IsFullPath(m.target) || *m.target == '/' || *m.target == '\\'))
|
|
target = m.target;
|
|
else
|
|
if(m.target_override && !IsNull(m.target))
|
|
target = host->NormalizePath(AppendFileName(tout, m.target));
|
|
else
|
|
if(IsFullPath(mainfn))
|
|
target = mainfn;
|
|
else
|
|
target = host->NormalizePath(AppendFileName(tout, mainfn));
|
|
}
|
|
b->target = target;
|
|
if(IsNull(onefile)) {
|
|
String out;
|
|
out << "----- " << package << " ( " << Join(b->config.GetKeys(), " ") << " )";
|
|
if(pkcount > 1)
|
|
out << " (" << (pknumber + 1) << " / " << pkcount << ')';
|
|
PutConsole(out);
|
|
}
|
|
else
|
|
b->config.FindAdd("NOLIB");
|
|
bool ok = b->BuildPackage(package, linkfile, linkopt,
|
|
GetAllUses(wspc, pkindex),
|
|
GetAllLibraries(wspc, pkindex, bm, mainparam, *host, *b),
|
|
targetmode - 1);
|
|
Vector<String> errors = console.PickErrors();
|
|
host->DeleteFile(errors);
|
|
if(!ok || !errors.IsEmpty())
|
|
return false;
|
|
if(link) {
|
|
ok = b->Link(linkfile, linkopt, GetTargetMode().createmap);
|
|
errors = console.PickErrors();
|
|
host->DeleteFile(errors);
|
|
if(!ok || !errors.IsEmpty())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Ide::SetHdependDirs()
|
|
{
|
|
HdependSetDirs(SplitDirs(GetVar("UPP") + ';'
|
|
+ GetMethodVars(method).Get("INCLUDE", "") + ';'
|
|
+ Environment().Get("INCLUDE", "")));
|
|
}
|
|
|
|
void Ide::BeginBuilding(bool sync_files, bool clear_console)
|
|
{
|
|
SetupDefaultMethod();
|
|
HdependTimeDirty();
|
|
Renumber();
|
|
ShowConsole();
|
|
StopDebug();
|
|
SaveFile();
|
|
SaveWorkspace();
|
|
SetIdeState(BUILDING);
|
|
console.Kill();
|
|
console.ClearError();
|
|
if(clear_console)
|
|
console.Clear();
|
|
build_time = GetTickCount();
|
|
CreateHost(sync_files);
|
|
cmdout.Clear();
|
|
}
|
|
|
|
void Ide::EndBuilding(bool ok)
|
|
{
|
|
console.EndGroup();
|
|
console.Wait();
|
|
Vector<String> errors = console.PickErrors();
|
|
CreateHost(false)->DeleteFile(errors);
|
|
if(!errors.IsEmpty())
|
|
ok = false;
|
|
PutConsole("");
|
|
PutConsole((ok ? "OK. " : "There were errors. ") + GetPrintTime(build_time));
|
|
SetIdeState(EDITING);
|
|
if(GetTopWindow()->IsOpen())
|
|
if(ok)
|
|
BeepMuteInformation();
|
|
else
|
|
BeepMuteExclamation();
|
|
}
|
|
|
|
Vector<String> Ide::GetAllUses(const Workspace& wspc, int f)
|
|
{
|
|
String package = wspc[f];
|
|
Index<String> all_uses;
|
|
bool warn = true;
|
|
int n = 0;
|
|
while(f >= 0) {
|
|
const Package& p = wspc.package[f];
|
|
for(int fu = 0; fu < p.uses.GetCount(); fu++)
|
|
if(p.uses[fu].text != package)
|
|
all_uses.FindAdd(p.uses[fu].text);
|
|
else if(warn) {
|
|
::PutConsole(NFormat("%s: circular 'uses' chain", package));
|
|
warn = false;
|
|
}
|
|
f = -1;
|
|
while(n < all_uses.GetCount() && (f = wspc.package.Find(all_uses[n++])) < 0)
|
|
;
|
|
}
|
|
return all_uses.PickKeys();
|
|
}
|
|
|
|
Vector<String> Ide::GetAllLibraries(const Workspace& wspc, int index,
|
|
const VectorMap<String, String>& bm, String mainparam,
|
|
Host& host, Builder& builder)
|
|
{
|
|
Vector<String> uses = GetAllUses(wspc, index);
|
|
uses.Add(wspc[index]);
|
|
Index<String> libraries;
|
|
for(int i = 0; i < uses.GetCount(); i++) {
|
|
int f = wspc.package.Find(uses[i]);
|
|
if(f >= 0) {
|
|
const Package& pk = wspc.package[f];
|
|
Index<String> config = PackageConfig(wspc, f, bm, mainparam, host, builder);
|
|
Vector<String> pklibs = Split(Gather(pk.library, config.GetKeys()), ' ');
|
|
FindAppend(libraries, pklibs);
|
|
}
|
|
}
|
|
return libraries.PickKeys();
|
|
}
|
|
|
|
bool Ide::Build(const Workspace& wspc, String mainparam, String outfile, bool clear_console)
|
|
{
|
|
ClearErrorEditor();
|
|
BeginBuilding(true, clear_console);
|
|
bool ok = true;
|
|
if(wspc.GetCount()) {
|
|
Vector<int> build_order;
|
|
if(GetTargetMode().linkmode != 2) {
|
|
for(int i = 1; i < wspc.GetCount(); i++)
|
|
build_order.Add(i);
|
|
}
|
|
else {
|
|
Index<int> remaining;
|
|
for(int i = 1; i < wspc.GetCount(); i++)
|
|
remaining.Add(i);
|
|
while(!remaining.IsEmpty()) {
|
|
int t;
|
|
for(t = 0; t < remaining.GetCount(); t++) {
|
|
const Package& pk = wspc.package[remaining[t]];
|
|
bool delay = false;
|
|
for(int u = 0; u < pk.uses.GetCount(); u++)
|
|
if(remaining.Find(wspc.package.Find(pk.uses[u].text)) >= 0) {
|
|
delay = true;
|
|
break;
|
|
}
|
|
if(!delay)
|
|
break;
|
|
}
|
|
if(t >= remaining.GetCount())
|
|
t = 0;
|
|
build_order.Add(remaining[t]);
|
|
remaining.Remove(t);
|
|
}
|
|
}
|
|
String mainpackage = wspc[0];
|
|
Vector<String> linkfile;
|
|
String linkopt;
|
|
ok = true;
|
|
int ms = msecs();
|
|
for(int i = 0; i < build_order.GetCount() && (ok || !stoponerrors); i++) {
|
|
int px = build_order[i];
|
|
ok = BuildPackage(wspc, px, i, build_order.GetCount() + 1,
|
|
mainparam, Null, linkfile, linkopt) && ok;
|
|
if(msecs() - ms >= 200) {
|
|
ProcessEvents();
|
|
ms = msecs();
|
|
}
|
|
}
|
|
if(ok || !stoponerrors)
|
|
ok = BuildPackage(wspc, 0, build_order.GetCount(), build_order.GetCount() + 1,
|
|
mainparam, outfile, linkfile, linkopt, ok) && ok;
|
|
}
|
|
EndBuilding(ok);
|
|
ReQualifyCodeBase();
|
|
SetErrorEditor();
|
|
return ok;
|
|
}
|
|
|
|
bool Ide::Build()
|
|
{
|
|
VectorMap<String, String> bm = GetMethodVars(method);
|
|
if(bm.GetCount() == 0) {
|
|
PutConsole("Invalid build method");
|
|
return false;
|
|
}
|
|
One<Host> host = CreateHost(false);
|
|
Index<String> p = PackageConfig(IdeWorkspace(), 0, bm, mainconfigparam,
|
|
*host, *CreateBuilder(~host));
|
|
Workspace wspc;
|
|
wspc.Scan(main, p.GetKeys());
|
|
return Build(wspc, mainconfigparam, Null);
|
|
}
|
|
|
|
void Ide::DoBuild()
|
|
{
|
|
Build();
|
|
}
|
|
|
|
void Ide::PackageBuild()
|
|
{
|
|
BeginBuilding();
|
|
int pi = package.GetCursor();
|
|
const Workspace& wspc = IdeWorkspace();
|
|
if(pi >= 0 && pi <= wspc.GetCount()) {
|
|
Vector<String> linkfile;
|
|
String linkopt;
|
|
bool ok = BuildPackage(wspc, pi, 0, 1, mainconfigparam, Null, linkfile, linkopt);
|
|
EndBuilding(ok);
|
|
}
|
|
}
|
|
|
|
void Ide::StopBuild()
|
|
{
|
|
if(idestate == BUILDING) {
|
|
console.Kill();
|
|
PutConsole("User break.");
|
|
SetIdeState(EDITING);
|
|
}
|
|
}
|
|
|
|
String Ide::GetOutputDir()
|
|
{
|
|
return GetFileFolder(target);
|
|
}
|
|
|
|
void Ide::CleanPackage(const Workspace& wspc, int package)
|
|
{
|
|
PutConsole(NFormat("Cleaning %s", wspc[package]));
|
|
One<Host> host = CreateHost(false);
|
|
host->DeleteFolderDeep(OutDir(PackageConfig(wspc, package, GetMethodVars(method), mainconfigparam,
|
|
*host, *CreateBuilder(~host)), wspc[package], GetMethodVars(method)));
|
|
}
|
|
|
|
void Ide::Clean()
|
|
{
|
|
console.Clear();
|
|
const Workspace& wspc = IdeWorkspace();
|
|
for(int i = 0; i < wspc.GetCount(); i++)
|
|
CleanPackage(wspc, i);
|
|
PutConsole("...done");
|
|
}
|
|
|
|
void Ide::PackageClean()
|
|
{
|
|
int pi = package.GetCursor();
|
|
const Workspace& wspc = IdeWorkspace();
|
|
if(pi >= 0 && pi < wspc.GetCount()) {
|
|
console.Clear();
|
|
CleanPackage(wspc, pi);
|
|
}
|
|
}
|
|
|
|
void Ide::RebuildAll()
|
|
{
|
|
Clean();
|
|
Build();
|
|
}
|
|
|
|
void Ide::CleanUppOut()
|
|
{
|
|
String out = GetVar("OUTPUT");
|
|
if(!PromptYesNo(NFormat("Erase the whole output directory [* \1%s\1]?", out)))
|
|
return;
|
|
console.Clear();
|
|
PutConsole("UPPOUT cleanup...");
|
|
DeleteFolderDeep(out);
|
|
PutConsole("(done)");
|
|
HideBottom();
|
|
}
|
|
|
|
void Ide::SwitchHeader() {
|
|
int c = filelist.GetCursor();
|
|
if(c < 0) return;
|
|
String currfile = filelist[c];
|
|
const char *ext = GetFileExtPos(currfile);
|
|
if(!stricmp(ext, ".h") || !stricmp(ext, ".hpp")
|
|
|| !stricmp(ext, ".lay") || !stricmp(ext, ".iml")) {
|
|
int f = filelist.Find(ForceExt(currfile, ".cpp"));
|
|
if(f < 0) f = filelist.Find(ForceExt(currfile, ".c"));
|
|
if(f < 0) f = filelist.Find(ForceExt(currfile, ".cc"));
|
|
if(f >= 0) filelist.SetCursor(f);
|
|
}
|
|
}
|
|
|
|
void Ide::FileCompile()
|
|
{
|
|
if(editfile.IsEmpty())
|
|
return;
|
|
ClearErrorEditor(editfile);
|
|
SwitchHeader();
|
|
BeginBuilding();
|
|
const Workspace& wspc = IdeWorkspace();
|
|
bool ok = true;
|
|
onefile = editfile;
|
|
if(wspc.GetCount()) {
|
|
Vector<String> linkfile;
|
|
String linkopt;
|
|
for(int i = 0; i < wspc.GetCount(); i++)
|
|
BuildPackage(wspc, i, 1, wspc.GetCount(), mainconfigparam, Null, linkfile, linkopt, false);
|
|
}
|
|
onefile.Clear();
|
|
EndBuilding(ok);
|
|
SetErrorEditor();
|
|
}
|
|
|
|
void Ide::Preprocess(bool asmout) {
|
|
if(editfile.IsEmpty())
|
|
return;
|
|
int pi = package.GetCursor();
|
|
if(pi < 0) return;
|
|
SwitchHeader();
|
|
String pfn = ConfigFile(GetFileTitle(editfile) + ".i.tmp");
|
|
DeleteFile(pfn);
|
|
const Workspace& wspc = IdeWorkspace();
|
|
if(pi >= wspc.GetCount())
|
|
return;
|
|
One<Host> host = CreateHost(true);
|
|
One<Builder> b = CreateBuilder(~host);
|
|
Vector<String> linkfile;
|
|
String linkopt;
|
|
b->config = PackageConfig(wspc, pi, GetMethodVars(method), mainconfigparam, *host, *b);
|
|
console.Clear();
|
|
PutConsole((asmout ? "Compiling " : "Preprocessing ") + editfile);
|
|
b->Preprocess(wspc[pi], editfile, pfn, asmout);
|
|
HideBottom();
|
|
if(FileExists(pfn)) {
|
|
EditFile(pfn);
|
|
if(!editor.IsReadOnly())
|
|
ToggleReadOnly();
|
|
}
|
|
}
|
|
|
|
void Ide::CreateMakefile()
|
|
{
|
|
const Workspace& wspc = IdeWorkspace();
|
|
if(wspc.GetCount() == 0) {
|
|
PutConsole("Project is empty!");
|
|
return;
|
|
}
|
|
FileSel mfout;
|
|
mfout.AllFilesType();
|
|
mfout <<= AppendFileName(GetFileDirectory(PackagePath(wspc[0])), "Makefile");
|
|
if(!mfout.ExecuteSaveAs("Save makefile as"))
|
|
return;
|
|
SaveMakeFile(~mfout);
|
|
}
|
|
|
|
void Ide::SaveMakeFile(const String& fn, bool exporting)
|
|
{
|
|
const Workspace& wspc = IdeWorkspace();
|
|
|
|
BeginBuilding(false);
|
|
|
|
VectorMap<String, String> bm = GetMethodVars(method);
|
|
One<Host> host = CreateHost(false);
|
|
One<Builder> b = CreateBuilder(~host);
|
|
const TargetMode& tm = GetTargetMode();
|
|
|
|
String makefile;
|
|
|
|
Vector<String> uppdirs = GetUppDirs();
|
|
String uppout = exporting ? host->GetHostPath(GetVar("OUTPUT")) : "_out/";
|
|
String inclist;
|
|
|
|
Index<String> allconfig = PackageConfig(wspc, 0, bm, mainconfigparam, *host, *b);
|
|
bool win32 = allconfig.Find("WIN32") >= 0;
|
|
|
|
for(int i = 1; i < wspc.GetCount(); i++) {
|
|
Index<String> modconfig = PackageConfig(wspc, i, bm, mainconfigparam, *host, *b);
|
|
for(int a = allconfig.GetCount(); --a >= 0;)
|
|
if(modconfig.Find(allconfig[a]) < 0)
|
|
allconfig.Remove(a);
|
|
}
|
|
|
|
if(!exporting)
|
|
for(int i = 0; i < uppdirs.GetCount(); i++) {
|
|
String srcdir = GetMakePath(AdjustMakePath(host->GetHostPath(AppendFileName(uppdirs[i], ""))), win32);
|
|
makefile << "UPPDIR" << (i + 1) << " = " << srcdir << "\n";
|
|
inclist << " -I$(UPPDIR" << (i + 1) << ")";
|
|
}
|
|
else
|
|
inclist << "-I./";
|
|
Vector<String> includes = SplitDirs(bm.Get("INCLUDE",""));
|
|
for(int i = 0; i < includes.GetCount(); i++)
|
|
inclist << " -I" << includes[i];
|
|
|
|
makefile << "\n"
|
|
"UPPOUT = " << (exporting ? "_out/" : GetMakePath(AdjustMakePath(host->GetHostPath(AppendFileName(uppout, ""))), win32)) << "\n"
|
|
"CINC = " << inclist << "\n"
|
|
"Macro = ";
|
|
|
|
for(int i = 0; i < allconfig.GetCount(); i++)
|
|
makefile << " -Dflag" << allconfig[i];
|
|
makefile << "\n";
|
|
|
|
String output, config, install, rules, linkdep, linkfiles, linkfileend;
|
|
|
|
for(int i = 0; i < wspc.GetCount(); i++) {
|
|
b->config = PackageConfig(wspc, i, bm, mainconfigparam, *host, *b);
|
|
b->version = tm.version;
|
|
b->method = method;
|
|
MakeFile mf;
|
|
b->AddMakeFile(mf, wspc[i], GetAllUses(wspc, i),
|
|
GetAllLibraries(wspc, i, bm, mainconfigparam, *host, *b), allconfig,
|
|
exporting);
|
|
if(!i) {
|
|
String tdir = mf.outdir;
|
|
String trg;
|
|
if(tm.target_override) {
|
|
trg = GetMakePath(AdjustMakePath(tm.target), win32);
|
|
if(!trg.IsEmpty() && *trg.Last() == (win32 ? '\\' : '/'))
|
|
trg << mf.outfile;
|
|
else if(trg.Find(win32 ? '\\' : '/') < 0)
|
|
trg.Insert(0, "$(OutDir)");
|
|
}
|
|
output = Nvl(trg, mf.output);
|
|
if(exporting)
|
|
output = wspc[i] + ".out";
|
|
install << "\n"
|
|
"OutDir = " << tdir << "\n"
|
|
"OutFile = " << output << "\n"
|
|
"\n"
|
|
".PHONY: all\n"
|
|
"all: install $(OutFile)\n"
|
|
"\n"
|
|
".PHONY: install\n"
|
|
"install:\n";
|
|
}
|
|
config << mf.config;
|
|
install << mf.install;
|
|
rules << mf.rules;
|
|
linkdep << mf.linkdep;
|
|
linkfiles << mf.linkfiles;
|
|
linkfileend << mf.linkfileend;
|
|
}
|
|
|
|
makefile
|
|
<< config
|
|
<< install
|
|
<< "\n"
|
|
"$(OutFile): " << linkdep << "\n\t" << linkfiles << linkfileend << "\n"
|
|
<< rules;
|
|
|
|
bool sv = ::SaveFile(fn, makefile);
|
|
if(!exporting)
|
|
if(sv)
|
|
PutConsole(NFormat("%s(1): makefile generation complete", fn));
|
|
else
|
|
PutConsole(NFormat("%s: error writing makefile", fn));
|
|
|
|
EndBuilding(true);
|
|
}
|
|
|
|
#if defined(PLATFORM_WIN32) || defined(PLATFORM_LINUX)
|
|
void Ide::OpenOutputFolder()
|
|
{
|
|
#if defined(PLATFORM_WIN32)
|
|
LaunchWebBrowser(GetFileFolder(target));
|
|
#else
|
|
system("xdg-open " + GetFileFolder(target) + " &");
|
|
#endif
|
|
}
|
|
#endif
|
|
|