#include "Builders.h" void PutCompileTime(int time, int count) { PutConsole(String().Cat() << count << " file(s) compiled in " << GetPrintTime(time) << " " << int(GetTickCount() - time) / count << " msec/file"); } String CppBuilder::GetTargetExt() const { if(!HasFlag("WIN32") && !HasFlag("WINCE")) return HasFlag("DLL") ? ".so" : ""; else return HasFlag("DLL") ? ".dll" : ".exe"; } String CppBuilder::GetSharedLibPath(const String& package) const { String outfn; for(const char *p = package; *p; p++) outfn.Cat(IsAlNum(*p) || *p == '-' ? *p : '_'); if(!IsNull(version)) outfn << version; outfn << (HasFlag("WIN32") || HasFlag("WINCE") ? ".dll" : ".so"); return CatAnyPath(GetFileFolder(target), outfn); } String CppBuilder::GetHostPath(const String& path) const { return host->GetHostPath(path); } String CppBuilder::GetHostPathShort(const String& path) const { #ifdef PLATFORM_WIN32 const dword SHORT_PATH_LENGTH = 2048; char short_path[SHORT_PATH_LENGTH]; dword length = ::GetShortPathName((LPCTSTR) path, (LPTSTR) short_path, SHORT_PATH_LENGTH); if(length > 0) return String(short_path, length); #endif return path; } String CppBuilder::GetHostPathQ(const String& path) const { return '\"' + GetHostPath(path) + '\"'; } String CppBuilder::GetHostPathShortQ(const String& path) const { return '\"' + GetHostPathShort(path) + '\"'; } String CppBuilder::GetLocalPath(const String& path) const { return host->GetLocalPath(path); } Vector CppBuilder::GetFileInfo(const Vector& path) const { return host->GetFileInfo(path); } Host::FileInfo CppBuilder::GetFileInfo(const String& path) const { return GetFileInfo(Vector() << path)[0]; } Time CppBuilder::GetFileTime(const String& path) const { return GetFileInfo(path); } void CppBuilder::DeleteFile(const Vector& path) { host->DeleteFile(path); } void CppBuilder::DeleteFile(const String& path) { host->DeleteFile(Vector() << path); } int CppBuilder::Execute(const char *cmdline) { return host->Execute(cmdline); } int CppBuilder::Execute(const char *cl, Stream& out) { return host->Execute(cl, out); } int CppBuilder::AllocSlot() { return host->AllocSlot(); } bool CppBuilder::Run(const char *cmdline, int slot, String key, int blitz_count) { return host->Run(cmdline, slot, key, blitz_count); } bool CppBuilder::Run(const char *cmdline, Stream& out, int slot, String key, int blitz_count) { return host->Run(cmdline, out, slot, key, blitz_count); } bool CppBuilder::Wait() { return host->Wait(); } void CppBuilder::ChDir(const String& path) { host->ChDir(path); } void CppBuilder::SaveFile(const String& path, const String& data) { host->SaveFile(path, data); } String CppBuilder::LoadFile(const String& path) { return host->LoadFile(path); } bool CppBuilder::FileExists(const String& path) const { return !IsNull(GetFileInfo(path).length); } int CasFilter(int c) { return c == '\n' ? '\n' : c == '\t' ? ' ' : c >= ' ' ? c : 0; } int CharFilterEol(int c) { return c == '\n' || c == '\r' ? c : 0; } Vector Cuprep(const String& m, const VectorMap& mac, const Vector& inc) { String r; String q = Filter(m, CasFilter); const char *s = q; while(*s) if(*s == '$') { String v; s++; if(*s == '$') { r.Cat('$'); s++; } else if(*s == '(') { s++; bool pinc = false; if(*s == '!') { pinc = true; s++; } for(;;) { if(*s == ')') { s++; break; } if(*s == '\0') break; v.Cat(*s++); } if(pinc) for(int i = 0; i < inc.GetCount(); i++) if(inc[i].Find(' ')) r << v << '"' << inc[i] << "\" "; else r << v << inc[i] << ' '; else r.Cat(mac.Get(v, Null)); } else r.Cat('$'); } else r.Cat(*s++); return Split(r, CharFilterTextTest(CharFilterEol)); } bool IsCd(const String& cmd) { if(cmd.GetLength() > 2 && ToLower(cmd.Mid(0, 3)) == "cd ") { #ifdef PLATFOTM_POSIX chdir(path); #endif #ifdef PLATFORM_WIN32 SetCurrentDirectory(cmd.Mid(3)); #endif return true; } return false; } static void AddPath(VectorMap& out, String key, String path) { out.Add(key, path); out.Add(key + "_WIN", WinPath(path)); out.Add(key + "_UNIX", UnixPath(path)); } Vector CppBuilder::CustomStep(const String& path) { String file = GetHostPath(path); String ext = ToLower(GetFileExt(file)); for(int i = 0; i < wspc.GetCount(); i++) { const Array< ::CustomStep >& mv = wspc.GetPackage(i).custom; for(int j = 0; j < mv.GetCount(); j++) { const ::CustomStep& m = mv[j]; if(MatchWhen(m.when, config.GetKeys()) && m.MatchExt(ext)) { VectorMap mac; AddPath(mac, "PATH", file); AddPath(mac, "DIR", GetFileFolder(file)); mac.Add("FILE", GetFileName(file)); mac.Add("TITLE", GetFileTitle(file)); AddPath(mac, "OUTPATH", GetHostPath(target)); AddPath(mac, "OUTDIR", GetHostPath(outdir)); mac.Add("OUTFILE", GetFileName(target)); mac.Add("OUTTITLE", GetFileTitle(target)); mac.Add("INCLUDE", Join(include, ";")); Vector out = Cuprep(m.output, mac, include); bool dirty = out.IsEmpty(); for(int i = 0; !dirty && i < out.GetCount(); i++) dirty = (GetFileTime(file) > GetFileTime(out[i])); if(dirty) { HdependTimeDirty(); PutConsole(GetFileName(file)); Vector cmd = Cuprep(m.command, mac, include); String cmdtext; for(int c = 0; c < cmd.GetCount(); c++) if(!IsCd(cmd[c])) { String ctext = cmd[c]; const char *cm = ctext; if(*cm == '?') cm++; if(*ctext != '?' && Execute(cm)) { for(int t = 0; t < out.GetCount(); t++) DeleteFile(out[t]); return Vector(); } } } return out; } } } Vector out; out.Add(path); return out; } static Time s_bb = Null; Time BlitzBaseTime() { if(IsNull(s_bb)) s_bb = GetSysTime(); return max(GetSysTime() - 3600, s_bb); } Blitz CppBuilder::BlitzStep(Vector& sfile, Vector& soptions, Vector& obj, const char *objext, Vector& optimize) { Blitz b; Time now = GetSysTime(); Vector excluded; Vector excludedoptions; Vector excludedoptimize; b.object = CatAnyPath(outdir, "$blitz" + String(objext)); Time blitztime = GetFileTime(b.object); String blitz; b.count = 0; b.build = false; if(!IdeGetOneFile().IsEmpty()) return b; for(int i = 0; i < sfile.GetCount(); i++) { String fn = sfile[i]; String ext = ToLower(GetFileExt(fn)); String objfile = CatAnyPath(outdir, GetFileTitle(fn) + objext); Time fntime = GetFileTime(fn); if((ext == ".cpp" || ext == ".cc" || ext == ".cxx") && HdependBlitzApproved(fn) && IsNull(soptions[i]) && !optimize[i] // && (fntime < blitztime || !blitzexists) // && (!FileExists(objfile) || now - fntime > 3600)) { // Causes a strage oscillation && fntime < BlitzBaseTime()) { if(HdependFileTime(fn) > blitztime) b.build = true; blitz << "\r\n" << "#define BLITZ_INDEX__ F" << i << "\r\n" << "#include \"" << GetHostPath(fn) << "\"\r\n"; b.info << ' ' << GetFileName(fn); const Vector& d = HdependGetDefines(fn); for(int i = 0; i < d.GetCount(); i++) blitz << "#ifdef " << d[i] << "\r\n" << "#undef " << d[i] << "\r\n" << "#endif\r\n"; blitz << "#undef BLITZ_INDEX__\r\n"; b.count++; } else { excluded.Add(fn); excludedoptions.Add(soptions[i]); excludedoptimize.Add(optimize[i]); } } b.path = CatAnyPath(outdir, "$blitz.cpp"); if(b.count > 1) { sfile = excluded; soptions = excludedoptions; optimize = excludedoptimize; if(LoadFile(b.path) != blitz) { SaveFile(b.path, blitz); b.build = true; } obj.Add(b.object); } else { DeleteFile(b.path); b.build = false; } return b; } String CppBuilder::Includes(const char *sep) { String cc; for(int i = 0; i < include.GetCount(); i++) cc << sep << GetHostPathQ(include[i]); return cc; } String CppBuilder::IncludesShort(const char *sep) { String cc; for(int i = 0; i < include.GetCount(); i++) cc << sep << GetHostPathShortQ(include[i]); return cc; } String CppBuilder::IncludesDefinesTargetTime() { String cc = Includes(" -I"); for(int i = 0; i < config.GetCount(); i++) cc << " -Dflag" << config[i]; Time t = GetSysTime(); cc << " -DbmYEAR=" << (int)t.year; cc << " -DbmMONTH=" << (int)t.month; cc << " -DbmDAY=" << (int)t.day; cc << " -DbmHOUR=" << (int)t.hour; cc << " -DbmMINUTE=" << (int)t.minute; cc << " -DbmSECOND=" << (int)t.second; targettime = GetFileTime(target); return cc; } static String MakeIdent(const char *name) { String out; for(; *name; name++) out << (iscid(*name) ? *name : '_'); return out; } static String MakeSourcePath(const Vector& dirs, String fn, bool raw, bool exporting) { fn = UnixPath(fn); for(int i = 0; i < dirs.GetCount(); i++) { int dl = dirs[i].GetLength(); if(fn.GetLength() >= dl + 2 && !memcmp(fn, dirs[i], dl) && fn[dl] == '/') { String s; if(!exporting) s << "$(UPPDIR" << (i + 1) << ")"; s << AdjustMakePath(fn.GetIter(dl + 1)); return s; } } return raw ? String() : AdjustMakePath(fn); } String CppBuilder::GetMakePath(String fn) const { return ::GetMakePath(fn, HasFlag("WIN32")); } void CppBuilder::AddMakeFile(MakeFile& makefile, String package, const Vector& all_uses, const Vector& all_libraries, const Index& common_config, bool exporting) { String packagepath = PackagePath(package); Package pkg; pkg.Load(packagepath); String packagedir = GetFileFolder(packagepath); Vector src = GetUppDirs(); for(int i = 0; i < src.GetCount(); i++) src[i] = UnixPath(src[i]); bool main = HasFlag("MAIN"); bool is_shared = HasFlag("SO"); bool libout = !main && !HasFlag("NOLIB"); bool win32 = HasFlag("WIN32"); String pack_ident = MakeIdent(package); String outdir = "OutDir_" + pack_ident; String macros = "Macro_" + pack_ident; String macdef = "$(Macro)"; String objext = (HasFlag("MSC") || HasFlag("EVC") ? ".obj" : ".o"); Vector x(config.GetKeys(), 1); Sort(x); for(int i = 0; i < x.GetCount(); i++) { if(common_config.Find(x[i]) < 0) macdef << " -Dflag" << x[i]; x[i] = InitCaps(x[i]); } makefile.outdir << "$(" << outdir << ")"; makefile.outfile << AdjustMakePath(GetFileTitle(NativePath(package))); if(main) makefile.outfile << GetTargetExt(); else if(is_shared) makefile.outfile << (win32 ? ".dll" : ".so"); else makefile.outfile << (win32 && HasFlag("MSC") ? ".lib" : ".a"); makefile.output << (main ? String("$(OutDir)") : makefile.outdir) << makefile.outfile; if(main) { String cc = "c++ -c"; if(HasFlag("DEBUG")) cc << " -D_DEBUG " << debug_options; else cc << ' ' << release_options; if(HasFlag("DEBUG_MINIMAL")) cc << " -ggdb -g1"; if(HasFlag("DEBUG_FULL")) cc << " -ggdb -g2"; if(is_shared && !win32) cc << " -fPIC "; cc << ' ' << Gather(pkg.option, config.GetKeys()); makefile.config << "CC = " << cc << "\n" "CFLAGS = $(CC) -x c\n" "CPPFLAGS = $(CC) -x c++ \n" "LIBPATH = "; for(int i = 0; i < libpath.GetCount(); i++) makefile.config << " -L" << GetMakePath(AdjustMakePath(GetHostPathQ(libpath[i]))); makefile.config << "\n" "AR = ar -sr\n"; makefile.install << "\t-mkdir -p $(OutDir)\n"; Vector lib; String lnk; lnk << "c++"; if(!HasFlag("SHARED")) lnk << " -static"; if(HasFlag("WIN32")) { lnk << " -mwindows"; if(!HasFlag("GUI")) makefile.linkfiles << " -mconsole"; } lnk << " -o $(OutFile)"; if(HasFlag("DEBUG") || HasFlag("DEBUG_MINIMAL") || HasFlag("DEBUG_FULL")) lnk << " -ggdb"; else lnk << (!HasFlag("OSX11") ? " -Wl,-s" : ""); lnk << " $(LIBPATH)"; if (!HasFlag("OSX11")) lnk << " -Wl,-O,2"; lnk << " $(LINKOPTIONS)"; makefile.linkfiles = lnk; } makefile.config << outdir << " = $(UPPOUT)" << GetMakePath(AdjustMakePath(String().Cat() << package << '/' << method << '-' << Join(x, "-") << '/')) << "\n" << macros << " = " << macdef << "\n"; makefile.install << "\t-mkdir -p $(" << outdir << ")\n"; String libdep, libfiles; libdep << makefile.output << ":"; if(is_shared) { libfiles = "c++ -shared -fPIC"; // -v"; Point p = ExtractVersion(); if(!IsNull(p.x)) { libfiles << " -Xlinker --major-image-version -Xlinker " << p.x; if(!IsNull(p.y)) libfiles << " -Xlinker --minor-image-version -Xlinker " << p.y; } libfiles << " -o "; } else libfiles = "$(AR) "; libfiles << makefile.output; for(int i = 0; i < pkg.GetCount(); i++) if(!pkg[i].separator) { String gop = Gather(pkg[i].option, config.GetKeys()); String fn = SourcePath(package, pkg[i]); String ext = ToLower(GetFileExt(fn)); bool isc = ext == ".c"; bool isrc = (ext == ".rc" && HasFlag("WIN32")); bool iscpp = (ext == ".cpp" || ext == ".cc" || ext == ".cxx"); bool isicpp = (ext == ".icpp"); if(isc || isrc || iscpp || isicpp) { String outfile; outfile << makefile.outdir << AdjustMakePath(GetFileTitle(fn)) << (isrc ? "_rc" : "") << objext; String srcfile = GetMakePath(MakeSourcePath(src, fn, false, exporting)); makefile.rules << outfile << ": " << srcfile; Vector dep = HdependGetDependencies(fn); Sort(dep, GetLanguageInfo()); for(int d = 0; d < dep.GetCount(); d++) { String dfn = MakeSourcePath(src, dep[d], true, exporting); if(!IsNull(dfn)) makefile.rules << " \\\n\t" << GetMakePath(dfn); } makefile.rules << "\n" "\t" << (isc ? "$(CFLAGS)" : "$(CPPFLAGS)") << " $(CINC) $(" << macros << ") " << gop << " " << srcfile << " -o " << outfile << "\n\n"; if(!libout || isicpp) { makefile.linkdep << " \\\n\t" << outfile; makefile.linkfiles << " \\\n\t\t" << outfile; } else { libdep << " \\\n\t" << outfile; libfiles << " \\\n\t\t" << outfile; } } else if(ext == ".o" || ext == ".obj" || ext == ".a" || ext == ".so" || ext == ".lib" || ext == ".dll") { makefile.linkdep << " \\\n\t" << fn; makefile.linkfiles << ' ' << fn; } } if(libout) { makefile.rules << libdep << "\n\t" << libfiles << "\n\n"; makefile.linkdep << " \\\n\t" << makefile.output; makefile.linkfiles << " \\\n\t\t\t" << makefile.output; } if(main) { if(!HasFlag("SOLARIS")&&!HasFlag("OSX11")) makefile.linkfiles << " \\\n\t\t-Wl,--start-group "; for(int i = 0; i < all_libraries.GetCount(); i++) { String ln = all_libraries[i]; String ext = ToLower(GetFileExt(ln)); if(ext == ".a" || ext == ".so" || ext == ".dll") makefile.linkfileend << " \\\n\t\t\t" << GetHostPathQ(FindInDirs(libpath, ln)); else makefile.linkfileend << " \\\n\t\t\t-l" << ln; } if(!HasFlag("SOLARIS")&&!HasFlag("OSX11")) makefile.linkfileend << " \\\n\t\t-Wl,--end-group\n\n"; } } Point CppBuilder::ExtractVersion() { Point v = Point(Null, Null); CParser p(version); while(!p.IsEof()) { if(p.IsNumber()) { v.x = p.ReadNumber(); break; } p.GetChar(); p.Spaces(); } while(!p.IsEof()) { if(p.IsNumber()) { v.y = p.ReadNumber(); break; } p.GetChar(); p.Spaces(); } return v; } void CppBuilder::ShowTime(int count, int start_time) { if(count) PutConsole(NFormat("%d file(s) compiled in %s %d msec/file", count, GetPrintTime(start_time), msecs(start_time) / count)); }