diff --git a/uppsrc/ide/Builders/Build.h b/uppsrc/ide/Builders/Build.h index b62686067..66c2354c9 100644 --- a/uppsrc/ide/Builders/Build.h +++ b/uppsrc/ide/Builders/Build.h @@ -25,6 +25,8 @@ String NoCr(const char *s); int CharFilterSlash(int c); String GetDefaultMethod(); +String GetMethodName(const String& method); +String GetMethodPath(const String& method); VectorMap GetMethodVars(const String& method); String MainConf(const Workspace& wspc, String& add_includes); @@ -95,7 +97,10 @@ public: void Clean(); void RebuildAll(); void SaveMakeFile(const String& fn, bool exporting); - void SaveCCJ(const String& fn, bool exporting); + + Array GetCompileCommands(); + + void SaveCCJ(const String& fn, bool exporting); MakeBuild(); diff --git a/uppsrc/ide/Builders/Builders.h b/uppsrc/ide/Builders/Builders.h index 09e9c6ccf..b7a4be042 100644 --- a/uppsrc/ide/Builders/Builders.h +++ b/uppsrc/ide/Builders/Builders.h @@ -17,8 +17,8 @@ Vector RepoInfo(const String& package); String MakeIdent(const char *name); struct CppBuilder : Builder { - virtual String GetTargetExt() const; - virtual void CleanPackage(const String& package, const String& outdir); + String GetTargetExt() const override; + void CleanPackage(const String& package, const String& outdir) override; const Workspace& wspc; Time targettime; @@ -38,8 +38,8 @@ struct CppBuilder : Builder { void DoRc(Vector& sfile, Vector& soptions, const Package& pkg, const String& package); String Includes(const char *sep, const String& package, const Package& pkg); - virtual String GetBuildInfoPath() const; - void SaveBuildInfo(const String& package); + String GetBuildInfoPath() const override; + void SaveBuildInfo(const String& package) override; String DefinesTargetTime(const char *sep, const String& package, const Package& pkg); String IncludesDefinesTargetTime(const String& package, const Package& pkg); bool HasAnyDebug() const; @@ -59,12 +59,14 @@ struct CppBuilder : Builder { void ShowTime(int count, int start_time); - virtual void AddMakeFile(MakeFile& makefile, String package, + void AddMakeFile(MakeFile& makefile, String package, const Vector& all_uses, const Vector& all_libraries, - const Index& common_config, bool exporting); + const Index& common_config, bool exporting) override; - virtual void AddCCJ(MakeFile& mfinfo, String package, - const Index& common_config, bool exporting, bool last_ws); + void AddCCJ(MakeFile& mfinfo, String package, + const Index& common_config, bool exporting, bool last_ws) override; + + void AddCommands(Array& commands, const String& package) override; CppBuilder() : wspc(GetIdeWorkspace()) {} }; @@ -171,4 +173,6 @@ INITIALIZE(JavaBuilder) INITIALIZE(AndroidBuilder) INITIALIZE(ScriptBuilder) +#include "ClangTidy.h" + #endif diff --git a/uppsrc/ide/Builders/Builders.upp b/uppsrc/ide/Builders/Builders.upp index aa51fd879..fa44c4f1c 100644 --- a/uppsrc/ide/Builders/Builders.upp +++ b/uppsrc/ide/Builders/Builders.upp @@ -16,7 +16,6 @@ file Builders.h options(BUILDER_OPTION) PCH, CppBuilder.cpp, MakeFile.cpp, - CCJ.cpp, coff.h, Builders readonly separator, GccBuilder.cpp, @@ -41,7 +40,11 @@ file Blitz.cpp, Build.h, Build.cpp, + CCJ.cpp, Install.cpp, + Clant-Tidy readonly separator, + ClangTidy.h, + ClangTidy.cpp, Helpers readonly separator, BuilderUtils.h, BuilderUtils.cpp, diff --git a/uppsrc/ide/Builders/CCJ.cpp b/uppsrc/ide/Builders/CCJ.cpp index 04315c28a..22bb5cc39 100644 --- a/uppsrc/ide/Builders/CCJ.cpp +++ b/uppsrc/ide/Builders/CCJ.cpp @@ -267,3 +267,96 @@ void MakeBuild::SaveCCJ(const String& fn, bool exporting) PutConsole(NFormat("%s: error writing compile_commands.json", fn)); } } + +void CppBuilder::AddCommands(Array& commands, const String& package) +{ + Package pkg; + pkg.Load(PackageFile(package)); + + String pack_ident = MakeIdent(package); + + String ci; + for(const String& m : include) + ci << " -I" << GetPathQ(UnixPath(m)); + + for(const String& m : config) + ci << " -Dflag" << m; + + for(int i = 0; i < pkg.GetCount(); i++) + if(!pkg[i].separator) { + String fn = SourcePath(package, pkg[i]); + String ext = ToLower(GetFileExt(fn)); + if(findarg(ext, ".c", ".cpp", ".cc", ".cxx", ".icpp") >= 0) { + String gop = Gather(pkg[i].option, config.GetKeys()); + + String outfile; + outfile << outdir << "/" << GetFileTitle(fn) << ".o"; + + CompileCommand& cc = commands.Add(); + cc.command << Nvl(compiler, "c++") << " -c" << " -x"; + if (ext == ".c") + cc.command << " c " << c_options; + else + cc.command << " c++ " << cpp_options; + cc.command << ci << ' ' << debug_options << ' ' << GetPathQ(UnixPath(fn)) << " -o" << GetPathQ(outfile); + cc.file = fn; + cc.ofile = outfile; + } + } +} + +Array MakeBuild::GetCompileCommands() +{ + Array commands; + VectorMap bm = GetMethodVars(method); + Host host; + CreateHost(host, false, false); + One b = CreateBuilder(&host); + + if(!b) + return commands; + + Index allconfig = PackageConfig(GetIdeWorkspace(), 0, bm, mainconfigparam, host, *b); + + Workspace wspc; + wspc.Scan(GetMain(), allconfig.GetKeys()); + + Index pkg_config; + for(int i = 0; i < wspc.GetCount(); i++) { + Index modconfig = PackageConfig(wspc, i, bm, mainconfigparam, host, *b); + PkgConfig(wspc, modconfig, pkg_config); + if(i) + for(int a = allconfig.GetCount(); --a >= 0;) + if(modconfig.Find(allconfig[a]) < 0) + allconfig.Remove(a); + } + + String incs; + + for(const String& s: pkg_config) { + String str; + String out; + str << "pkg-config --cflags " << s; + if (Sys(str, out) < 0) + continue; + out.TrimEnd("\n"); + if (out.IsEmpty()) + continue; + for(const String& m : Split(out, ' ')) + b->include << m; + } + + incs << ' ' << "-I" << GetPathQ(UnixPath(GetUppOut())); + + b->cc_inc = incs; + + for(int i = 0; i < wspc.GetCount(); i++) { + const String package = wspc[i]; + b->config = PackageConfig(wspc, i, bm, mainconfigparam, host, *b); + b->method = method; + b->outdir = UnixPath(OutDir(b->config, package, bm)); + b->AddCommands(commands, package); + } + + return commands; +} diff --git a/uppsrc/ide/Builders/ClangTidy.cpp b/uppsrc/ide/Builders/ClangTidy.cpp new file mode 100644 index 000000000..b1389f3e9 --- /dev/null +++ b/uppsrc/ide/Builders/ClangTidy.cpp @@ -0,0 +1,117 @@ +#include "Builders.h" + +#define LDUMP(x) // DDUMP(x) + +String ClangTidy::path; +Index ClangTidy::options; +Index ClangTidy::groups; +Index ClangTidy::active_checks; + +bool ClangTidy::HasClangTidy() +{ + ONCELOCK { +#ifdef PLATFORM_WIN32 + for(String p : Split(GetMethodVars("CLANGx64").Get("PATH", ""), ';')) { + p << "/clang-tidy.exe"; +#else + String p = "clang-tidy"; +#endif + String s = Sys(p + " -checks=* --list-checks"); + if(s.GetCount()) { + path = p; + for(String l : Split(s, '\n')) { + l = TrimBoth(l); + if(*l.Last() != ':') // Ignore "Enabled checks:" + options.FindAdd(l); + } + + for(String s : options) { + int q = s.Find('.'); + if(q < 0) + q = s.Find('-'); + if(q >= 0) + s.Trim(q); + groups.FindAdd(s); + } + Load(ClangTidyConfigPath()); + goto exit; // break not compatible with POSIX / ONCELOCK + } + +#ifdef PLATFORM_WIN32 + } +#endif + } +exit: + return path.GetCount() && options.GetCount(); +} + +String ClangTidy::ClangTidyConfigPath() { + return ConfigFile("ide-clang-tidy.json"); +} + +void ClangTidy::Load(const char *path) +{ + if(FileExists(path)) { + Value json = ParseJSON(LoadFile(path)); + active_checks.Clear(); + for(Value v : json["active_checks"]) + active_checks << ~v; + } + else + for(const String& s : options) { + if(s.StartsWith("clang-analyzer-core.")) + active_checks << s; + } +} + +void ClangTidy::Save(const char *path) +{ + Value json; + ValueArray va; + for(String s : active_checks) + va << s; + json("active_checks") = va; + SaveChangedFile(path, AsJSON(json, true)); +} + +void ClangTidy::RunClangTidy(const Array& commands) +{ + String cmdline; + + String win32_cmdline; // fix mingw-clang issue +#ifdef PLATFORM_WIN32 +// "C:/upp/bin/clang/bin/clang-tidy.exe" +// "C:/upp/bin/clang/include/c++/v1/typeinfo" + win32_cmdline = " -I" + GetPathQ(GetFileFolder(GetFileFolder(path)) + "/include/c++/v1"); +#endif + + JsonArray ccj; + String outdir; + for(const auto& m : commands) { + if(IsNull(outdir)) + outdir = GetFileFolder(m.ofile); + ccj << Upp::Json("directory", GetFileFolder(m.file)) + ("command", m.command + win32_cmdline) + ("file", GetFileName(m.file)); + cmdline << ' ' << GetPathQ(m.file); + LDUMP(cmdline); + } + + String cc_path = outdir + "/compile_commands.json"; + RealizePath(cc_path); + Upp::SaveFile(cc_path, ccj.ToString()); + + cmdline << " -checks=" << Join(active_checks.GetKeys(), ",") + << " -p " << cc_path; + + + String rf_path = outdir + "/clang_tidy_parameters_file"; + Upp::SaveFile(rf_path, cmdline); + + Host host; + MakeBuild *mb = dynamic_cast(TheIdeContext()); + if(mb) { + mb->CreateHost(host, false, false); + host.Execute(path + " @" + rf_path); + } +} diff --git a/uppsrc/ide/Builders/ClangTidy.h b/uppsrc/ide/Builders/ClangTidy.h new file mode 100644 index 000000000..e0f7e7814 --- /dev/null +++ b/uppsrc/ide/Builders/ClangTidy.h @@ -0,0 +1,13 @@ +struct ClangTidy { + static String path; + static Index options; + static Index groups; + static Index active_checks; + + static bool HasClangTidy(); + static String ClangTidyConfigPath(); + static void Load(const char *path); + static void Save(const char *path); + static void RunClangTidy(const Array& commands); +}; + diff --git a/uppsrc/ide/ClangTidy.cpp b/uppsrc/ide/ClangTidy.cpp new file mode 100644 index 000000000..2d9777173 --- /dev/null +++ b/uppsrc/ide/ClangTidy.cpp @@ -0,0 +1,197 @@ +#include "ide.h" + +struct ClangTidyDlg : WithClangTidyLayout, ClangTidy { + struct OptionWithLink : Option { + RichTextCtrl text; + }; + + ArrayMap checks; + + void SyncGroupCounts(); + void Group(); + void SetOptions(); + void ReadOptions(); + + ClangTidyDlg(); +}; + +ClangTidyDlg::ClangTidyDlg() +{ + CtrlLayoutOKCancel(*this, "Clang-Tidy"); + + group.AddIndex(); + group.AddColumn("Group"); + group.NoHeader(); + group.Add(Null, AttrText("All").Italic().NormalInk(SLtBlue())); + for(String s : groups) + group.Add(s, s); + + group.WhenSel = [=] { + Group(); + }; + + option.NoHeader(); + option.AddKey(); + option.AddColumn(); + option.NoCursor(); + + SetOptions(); + + auto Set = [=](bool b) { + for(int i = 0; i < option.GetCount(); i++) { + auto *o = dynamic_cast