diff --git a/CHANGES b/CHANGES index 27c3d2f5..423446e0 100644 --- a/CHANGES +++ b/CHANGES @@ -704,10 +704,23 @@ unreleased Version 0.3.41 - module list now shows x64 addresses correctly - verboser output for DLL load/unload - implement "set next statement" - - callstack: parameter list alo showed locals + - callstack: parameter list also showed locals - disassembly: show symbols, address labels - fix dstring display + - can list associative array members (needs + https://github.com/D-Programming-Language/dmd/pull/4473 for Win64) + - show exception number for Win32 exceptions + - can now load PDB symbols from Microsoft Symbol Server or local + cache folders + - now support lexical scope blocks (helps C++ and D with + https://github.com/D-Programming-Language/dmd/pull/2867) * cv2pdb: - improvements for DWARF conversion - * dparser updated to c89aa45334b8c768559bc243da73b87c518a9a58 with support for new + * dparser updated to cf26dbe426b10957f6364313cc7121e41083bebe with support for new language features in dmd 2.067 + * some initial support for finding symbol references + * new linker option "build and use local phobos library" to get a COFF32 version (dmd 2.067+) + or add missing debug info (dmd 2.065+) + * renaming project configuration did not change respective config on all platforms + * workaround for link response file with spaces, but disabled short names on NTFS volume + * profiler window: non-D symbols (e.g. C++ mangled functions) are no longer displayed empty \ No newline at end of file diff --git a/TODO b/TODO index 919af95e..535171db 100644 --- a/TODO +++ b/TODO @@ -124,13 +124,15 @@ Debugger: --------------- Any/dmd: - stepping through function call jumps to default assignment +- bad line info for scope(exit), for increment expression, constants - arguments to local functions treated as locals - local functions/delegates should have debug info for closure vars ("this" exists) - extern(D) function arguments shown in the wrong order - support GDC+mago -- bugzilla 14286: x64: no type information for dAssocArray ++ bugzilla 14286: x64: no type information for dAssocArray - bugzilla 4014: no debug info from libraries + Mago: - show derived class - step after exception @@ -150,14 +152,15 @@ Mago: - identically named locals show the same value + dstring shows as uint[] without string representation - string/wstring shown as char/wchar[], but with correct string representation -- AA indexing -- AA preview/expand ++ AA indexing ++ AA preview/expand - Array preview - delegate value/type - struct preview + verboser output when loading/unloading DLLs - attach to process - hardware breakpoints ++ does not work with variable scope VS: - VS2012: autoexp.dat does not work? depends on debug engine => edit and continue @@ -200,7 +203,7 @@ Unsorted - configuration of dependent project not read from configuration manager + jump to assertion error + import std.container: jump to package -- renaming configuration does not change project config ++ renaming configuration does not change project config + win32coff support - file search: replace . with \ in path search? + run cv2pdb disabled for GDC/x64 diff --git a/VERSION b/VERSION index 565f2431..392f0912 100644 --- a/VERSION +++ b/VERSION @@ -2,4 +2,4 @@ #define VERSION_MINOR 3 #define VERSION_REVISION 41 #define VERSION_BETA -beta -#define VERSION_BUILD 1 +#define VERSION_BUILD 2 diff --git a/doc/CppConversion.dd b/doc/CppConversion.dd index 6b506df9..a82e006f 100644 --- a/doc/CppConversion.dd +++ b/doc/CppConversion.dd @@ -103,6 +103,9 @@ class C { static int x; DECL *foo(int flags = 0); }; +int C::x = 3; +// moved with the implementation +DECL *C::foo(int flags) { return NULL; } , struct C { // comments are usually kept @@ -144,7 +147,7 @@ int foo() { Ident id = (op == kEnum) ? 0 : 1; } , - enum unnamed_1 { e1$_j e2 }alias unnamed_1 EX; + enum unnamed_1 { e1$(_j) e2 }alias unnamed_1 EX; enum ENUM { kEnum }; int foo() { Ident id = (op == ENUM.kEnum) ? 0 : 1; @@ -157,14 +160,14 @@ template<typename TYPE> struct ArrayBase : Array { TYPE* data; }; -template<typename S$_j class T> -double foo(S s$_j T t) { return s+t; } +template<typename S$(_j) class T> +double foo(S s$(_j) T t) { return s+t; } , class ArrayBase( TYPE) : Array { TYPE* data; }; -double foo( S$_j T)(S s$_j T t) { return s+t; } +double foo( S$(_j) T)(S s$(_j) T t) { return s+t; } ) Preprocessor macros are converted to enum, alias or templates diff --git a/vdc/abothe/Parser b/vdc/abothe/Parser index cf26dbe4..274940b2 160000 --- a/vdc/abothe/Parser +++ b/vdc/abothe/Parser @@ -1 +1 @@ -Subproject commit cf26dbe426b10957f6364313cc7121e41083bebe +Subproject commit 274940b28fa16fe648783361277f66350ce06da7 diff --git a/vdc/abothe/comserver/VDServer.cs b/vdc/abothe/comserver/VDServer.cs index fd9411e2..f97d6f58 100644 --- a/vdc/abothe/comserver/VDServer.cs +++ b/vdc/abothe/comserver/VDServer.cs @@ -65,9 +65,47 @@ protected override void AppendFormat (string content, StringBuilder sb, FormatFl sb.Append (content); //base.AppendFormat (content, sb, flags, r, g, b); } - } + } + + public class VDserverParseCacheView : ParseCacheView + { + #region Properties + readonly List packs; + #endregion + + #region Constructors + public VDserverParseCacheView(IEnumerable packageRoots) + { + this.packs = new List(); + Add(packageRoots); + } + + public VDserverParseCacheView(IEnumerable packages) + { + this.packs = new List(packages); + } + #endregion + + public override IEnumerable EnumRootPackagesSurroundingModule(DModule module) + { + return packs; + } + + public void Add(RootPackage pack) + { + if (pack != null && !packs.Contains(pack)) + packs.Add(pack); + } + + public void Add(IEnumerable roots) + { + RootPackage rp; + foreach (var r in roots) + if ((rp = GlobalParseCache.GetRootPackage(r)) != null && !packs.Contains(rp)) + packs.Add(rp); + } + } - [ComVisible(true), Guid(IID.VDServer)] [ClassInterface(ClassInterfaceType.None)] @@ -100,14 +138,52 @@ public DModule GetModule(string fileName) public VDServer() { // MessageBox.Show("VDServer()"); - } - - public void ConfigureSemanticProject(string filename, string imp, string stringImp, string versionids, string debugids, uint flags) + } + + private static string normalizePath(string path) + { + path = Path.GetFullPath(path); + return path.ToLower(); + } + + private static string normalizeDir(string dir) + { + dir = normalizePath(dir); + if (!dir.EndsWith("\\")) + dir += '\\'; + return dir; + } + + private string[] uniqueDirectories(string imp) + { + var impDirs = imp.Split('\n'); + string[] normDirs = new string[impDirs.Length]; + for (int i = 0; i < impDirs.Length; i++) + normDirs[i] = normalizeDir(impDirs[i]); + + string[] uniqueDirs = new string[impDirs.Length]; + int unique = 0; + for (int i = 0; i < normDirs.Length; i++) + { + int j; + for (j = 0; j < normDirs.Length; j++) + if (i != j && normDirs[i].StartsWith(normDirs[j])) + if (normDirs[i] != normDirs[j] || j < i) + break; + if (j >= normDirs.Length) + uniqueDirs[unique++] = normDirs[i]; + } + + Array.Resize(ref uniqueDirs, unique); + return uniqueDirs; + } + + public void ConfigureSemanticProject(string filename, string imp, string stringImp, string versionids, string debugids, uint flags) { if (_imports != imp) - { - var impDirs = imp.Split('\n'); - GlobalParseCache.BeginAddOrUpdatePaths(impDirs); + { + string[] uniqueDirs = uniqueDirectories(imp); + GlobalParseCache.BeginAddOrUpdatePaths(uniqueDirs); } _imports = imp; _stringImports = stringImp; @@ -124,7 +200,8 @@ public void ClearSemanticProject() //throw new NotImplementedException(); } public void UpdateModule(string filename, string srcText, bool verbose) - { + { + filename = normalizePath(filename); DModule ast; try { @@ -159,8 +236,9 @@ static int getCodeOffset(string s, CodeLocation loc) } public void GetTip(string filename, int startLine, int startIndex, int endLine, int endIndex) - { - var ast = GetModule(filename); + { + filename = normalizePath(filename); + var ast = GetModule(filename); if (ast == null) throw new COMException("module not found", 1); @@ -176,14 +254,14 @@ public void GetTip(string filename, int startLine, int startIndex, int endLine, // codeOffset+1 because otherwise it does not work on the first character _editorData.CaretOffset = getCodeOffset(_editorData.ModuleCode, _tipStart) + 1; - ISyntaxRegion sr; - DResolver.NodeResolutionAttempt attempt; - var types = DResolver.ResolveTypeLoosely(_editorData, out attempt, out sr); + ISyntaxRegion sr = DResolver.GetScopedCodeObject(_editorData); + LooseResolution.NodeResolutionAttempt attempt; + var types = sr != null ? LooseResolution.ResolveTypeLoosely(_editorData, sr, out attempt) : null; _tipText.Clear(); if (types != null) - { + { if (sr != null) { _tipStart = sr.Location; @@ -220,8 +298,9 @@ public void GetTipResult(out int startLine, out int startIndex, out int endLine, } public void GetSemanticExpansions(string filename, string tok, uint line, uint idx, string expr) - { - var ast = GetModule(filename); + { + filename = normalizePath(filename); + var ast = GetModule(filename); if (ast == null) throw new COMException("module not found", 1); @@ -254,8 +333,9 @@ public void GetSemanticExpansionsResult(out string stringList) } public void GetParseErrors(string filename, out string errors) - { - var ast = GetModule(filename); + { + filename = normalizePath(filename); + var ast = GetModule(filename); if (ast == null) throw new COMException("module not found", 1); @@ -276,6 +356,7 @@ public void GetParseErrors(string filename, out string errors) public void IsBinaryOperator(string filename, uint startLine, uint startIndex, uint endLine, uint endIndex, out bool pIsOp) { + filename = normalizePath(filename); var ast = GetModule(filename); if (ast == null) @@ -299,8 +380,9 @@ public void GetLastMessage(out string message) } public void GetDefinition(string filename, int startLine, int startIndex, int endLine, int endIndex) - { - var ast = GetModule(filename); + { + filename = normalizePath(filename); + var ast = GetModule(filename); if (ast == null) throw new COMException("module not found", 1); @@ -314,11 +396,11 @@ public void GetDefinition(string filename, int startLine, int startIndex, int en _editorData.SyntaxTree = ast as DModule; _editorData.ModuleCode = _sources[filename]; // codeOffset+1 because otherwise it does not work on the first character - _editorData.CaretOffset = getCodeOffset(_editorData.ModuleCode, _tipStart) + 2; - - ISyntaxRegion sr; - DResolver.NodeResolutionAttempt attempt; - var rr = DResolver.ResolveTypeLoosely(_editorData, out attempt, out sr); + _editorData.CaretOffset = getCodeOffset(_editorData.ModuleCode, _tipStart) + 2; + + ISyntaxRegion sr = DResolver.GetScopedCodeObject(_editorData); + LooseResolution.NodeResolutionAttempt attempt; + var rr = sr != null ? LooseResolution.ResolveTypeLoosely(_editorData, sr, out attempt) : null; _tipText.Clear(); if (rr != null) @@ -355,6 +437,7 @@ public void GetDefinitionResult(out int startLine, out int startIndex, out int e public void GetReferences(string filename, string tok, uint line, uint idx, string expr) { + filename = normalizePath(filename); var ast = GetModule(filename); if (ast == null) @@ -369,9 +452,9 @@ public void GetReferences(string filename, string tok, uint line, uint idx, stri _references = null; - ISyntaxRegion sr; - DResolver.NodeResolutionAttempt attempt; - var rr = DResolver.ResolveTypeLoosely(_editorData, out attempt, out sr); + ISyntaxRegion sr = DResolver.GetScopedCodeObject(_editorData); + LooseResolution.NodeResolutionAttempt attempt; + var rr = sr != null ? LooseResolution.ResolveTypeLoosely(_editorData, sr, out attempt) : null; StringBuilder refs = new StringBuilder(); if (rr != null) @@ -399,7 +482,7 @@ public void GetReferences(string filename, string tok, uint line, uint idx, stri private static void GetReferencesInModule(DModule ast, StringBuilder refs, DNode n, ResolutionContext ctxt) { - var res = ReferencesFinder.Scan(ast, n, ctxt); + var res = ReferencesFinder.SearchModuleForASTNodeReferences(ast, n, ctxt); int cnt = res.Count(); foreach (var r in res) @@ -439,9 +522,10 @@ void _setupEditorData() if ((_flags & 64) != 0) versions += "GNU\n"; else - versions += "DigitalMars\n"; - - _editorData.ParseCache = new ParseCacheView(_imports.Split('\n')); + versions += "DigitalMars\n"; + + string[] uniqueDirs = uniqueDirectories(_imports); + _editorData.ParseCache = new VDserverParseCacheView(uniqueDirs); _editorData.IsDebug = (_flags & 2) != 0; _editorData.DebugLevel = (_flags >> 16) & 0xff; _editorData.VersionNumber = (_flags >> 8) & 0xff; diff --git a/visuald/build.d b/visuald/build.d index dd0aef1f..ffa37059 100644 --- a/visuald/build.d +++ b/visuald/build.d @@ -286,6 +286,7 @@ else assert(std.string.indexOf(e.msg, "circular") >= 0); } } + ////////////////////////////////////////////////////////////////////// bool buildCustomFile(CFileNode file, ref bool built) { @@ -308,13 +309,44 @@ else } return true; } - + + ////////////////////////////////////////////////////////////////////// + bool buildPhobos(ref bool built) + { + string reason; + if(!mConfig.isPhobosUptodate(&reason)) + { + string cmdline = mConfig.GetPhobosCommandLine(); + if(cmdline.length) + { + string workdir = mConfig.GetProjectDir(); + string outfile = mConfig.GetPhobosPath(); + string cmdfile = makeFilenameAbsolute(outfile ~ "." ~ kCmdLogFileExtension, workdir); + showUptodateFailure(reason, outfile); + removeCachedFileTime(makeFilenameAbsolute(outfile, workdir)); + HRESULT hr = RunCustomBuildBatchFile(outfile, cmdfile, cmdline, m_pIVsOutputWindowPane, this); + if (hr != S_OK) + return false; // stop compiling + } + built = true; + } + return true; + } + /** build non-D files */ bool doCustomBuilds(out bool hasCustomBuilds, out int numCustomBuilds) { mixin(LogCallMix2); bool built; + if(mConfig.GetProjectOptions().privatePhobos) + { + if (!buildPhobos(built)) + return false; + if(built) + numCustomBuilds++; + } + // first build custom files with dependency graph CFileNode[] files = BuildDependencyList(); foreach(file; files) @@ -418,6 +450,10 @@ else bool customFilesUpToDate() { + if(mConfig.GetProjectOptions().privatePhobos) + if (!mConfig.isPhobosUptodate(null)) + return false; + CHierNode node = searchNode(mConfig.GetProjectNode(), delegate (CHierNode n) { @@ -1326,3 +1362,43 @@ unittest assert(match[2] == r"c:\\dmd\\phobos\\std\\utf.d"); } +bool launchBuildPhobos(string workdir, string cmdfile, string cmdline, IVsOutputWindowPane pane) +{ + ///////////// + auto srpIVsLaunchPadFactory = queryService!(IVsLaunchPadFactory); + if (!srpIVsLaunchPadFactory) + return false; + scope(exit) release(srpIVsLaunchPadFactory); + + ComPtr!(IVsLaunchPad) srpIVsLaunchPad; + HRESULT hr = srpIVsLaunchPadFactory.CreateLaunchPad(&srpIVsLaunchPad.ptr); + if(FAILED(hr) || !srpIVsLaunchPad.ptr) + return OutputErrorString(format("internal error: IVsLaunchPadFactory.CreateLaunchPad failed with rc=%x", hr)); + + try + { + std.file.write(cmdfile, cmdline); + } + catch(FileException e) + { + return OutputErrorString(format("internal error: cannot write file " ~ cmdfile ~ "\n")); + } + // scope(exit) std.file.remove(cmdfile); + + BSTR bstrOutput; + DWORD result; + hr = srpIVsLaunchPad.ExecCommand(/* [in] LPCOLESTR pszApplicationName */ _toUTF16z(getCmdPath()), + /* [in] LPCOLESTR pszCommandLine */ _toUTF16z("/Q /C " ~ quoteFilename(cmdfile)), + /* [in] LPCOLESTR pszWorkingDir */ _toUTF16z(workdir), // may be NULL, passed on to CreateProcess (wee Win32 API for details) + /* [in] LAUNCHPAD_FLAGS lpf */ LPF_PipeStdoutToOutputWindow, + /* [in] IVsOutputWindowPane *pOutputWindowPane */ pane, // if LPF_PipeStdoutToOutputWindow, which pane in the output window should the output be piped to + /* [in] ULONG nTaskItemCategory */ 0, // if LPF_PipeStdoutToTaskList is specified + /* [in] ULONG nTaskItemBitmap */ 0, // if LPF_PipeStdoutToTaskList is specified + /* [in] LPCOLESTR pszTaskListSubcategory */ null, // "Build"w.ptr, // if LPF_PipeStdoutToTaskList is specified + /* [in] IVsLaunchPadEvents *pVsLaunchPadEvents */ null, //pLaunchPadEvents, + /* [out] DWORD *pdwProcessExitCode */ &result, + /* [out] BSTR *pbstrOutput */ &bstrOutput); // all output generated (may be NULL) + + return hr == S_OK && result == 0; +} + diff --git a/visuald/config.d b/visuald/config.d index 1aba1aac..643af358 100644 --- a/visuald/config.d +++ b/visuald/config.d @@ -10,6 +10,7 @@ module visuald.config; import std.string; import std.conv; +import std.file; import std.path; import std.utf; import std.array; @@ -40,6 +41,7 @@ import visuald.propertypage; import visuald.stringutil; import visuald.fileutil; import visuald.lexutil; +import visuald.pkgutil; import visuald.vdextensions; // version = hasOutputGroup; @@ -237,6 +239,7 @@ class ProjectOptions string exefile; bool useStdLibPath; uint cRuntime; + bool privatePhobos; string additionalOptions; string preBuildCommand; @@ -301,28 +304,16 @@ class ProjectOptions case Compiler.LDC: return Package.GetGlobalOptions().LDC; } } - string buildDMDCommandLine(bool compile = true, bool performLink = true, bool deps = true, bool syntaxOnly = false) + + // common options with building phobos.lib + string dmdCommonCompileOptions() { string cmd; - if(otherDMD && program.length) - cmd = quoteNormalizeFilename(program); - else - cmd = "dmd"; - if(performLink && Package.GetGlobalOptions().demangleError) - cmd = "\"$(VisualDInstallDir)pipedmd.exe\" " ~ cmd; - - if(lib == OutputType.StaticLib && performLink) - cmd ~= " -lib"; - if(multiobj) - cmd ~= " -multiobj"; + if(isX86_64) cmd ~= " -m64"; else if(mscoff) cmd ~= " -m32mscoff"; - if(trace) - cmd ~= " -profile"; - if(quiet) - cmd ~= " -quiet"; if(verbose) cmd ~= " -v"; if(Dversion >= 2 && vtls) @@ -337,20 +328,12 @@ class ProjectOptions cmd ~= " -O"; if(useDeprecated) cmd ~= " -d"; - else if(errDeprecated) - cmd ~= " -de"; - if(Dversion >= 2 && noboundscheck) - cmd ~= " -noboundscheck"; - if(useUnitTests) - cmd ~= " -unittest"; if(useInline) cmd ~= " -inline"; if(release) cmd ~= " -release"; else cmd ~= " -debug"; - if(preservePaths) - cmd ~= " -op"; if(warnings) cmd ~= " -w"; if(infowarnings) @@ -359,6 +342,40 @@ class ProjectOptions cmd ~= " -property"; if(genStackFrame) cmd ~= " -gs"; + if(stackStomp) + cmd ~= " -gx"; + + return cmd; + } + + string buildDMDCommandLine(bool compile = true, bool performLink = true, bool deps = true, bool syntaxOnly = false) + { + string cmd; + if(otherDMD && program.length) + cmd = quoteNormalizeFilename(program); + else + cmd = "dmd"; + if(performLink && Package.GetGlobalOptions().demangleError) + cmd = "\"$(VisualDInstallDir)pipedmd.exe\" " ~ cmd; + + cmd ~= dmdCommonCompileOptions(); + + if(lib == OutputType.StaticLib && performLink) + cmd ~= " -lib"; + if(multiobj) + cmd ~= " -multiobj"; + if(trace) + cmd ~= " -profile"; + if(quiet) + cmd ~= " -quiet"; + else if(errDeprecated) + cmd ~= " -de"; + if(Dversion >= 2 && noboundscheck) + cmd ~= " -noboundscheck"; + if(useUnitTests) + cmd ~= " -unittest"; + if(preservePaths) + cmd ~= " -op"; if(cov) cmd ~= " -cov"; if(nofloat) @@ -367,8 +384,9 @@ class ProjectOptions cmd ~= " -ignore"; if(allinst) cmd ~= " -allinst"; - if(stackStomp) - cmd ~= " -gx"; + + if(privatePhobos) + cmd ~= " -defaultlib=" ~ quoteFilename(normalizeDir(outdir) ~ "privatephobos.lib"); if(doDocComments && compile && !syntaxOnly) { @@ -923,7 +941,7 @@ class ProjectOptions } } - string getCppCommandLine(string file) + string getCppCommandLine(string file, bool setenv) { int cc; // 0-3 for dmc,cl,clang,gdc switch(compiler) @@ -935,16 +953,17 @@ class ProjectOptions } string cmd = cccmd; - if(cc == 1) + if(cc == 1 && setenv) cmd = `call "%VCINSTALLDIR%\vcvarsall.bat" ` ~ (isX86_64 ? "x86_amd64" : "x86") ~ "\n" ~ cmd; static string[4] outObj = [ " -o", " -Fo", " -o", " -o " ]; - cmd ~= outObj[cc] ~ quoteFilename(file); + if (file.length) + cmd ~= outObj[cc] ~ quoteFilename(file); if (!ccTransOpt) return cmd; - static string[4] dbg = [ " -g", " -Zi", " -g", " -g" ]; + static string[4] dbg = [ " -g", " -Z7", " -g", " -g" ]; if(symdebug) cmd ~= dbg[cc]; @@ -955,6 +974,9 @@ class ProjectOptions if(optimize) cmd ~= opt[cc]; + if (quiet && cc == 1) + cmd ~= " /NOLOGO"; + return cmd; } @@ -1196,6 +1218,7 @@ class ProjectOptions elem ~= new xml.Element("exefile", toElem(exefile)); elem ~= new xml.Element("useStdLibPath", toElem(useStdLibPath)); elem ~= new xml.Element("cRuntime", toElem(cRuntime)); + elem ~= new xml.Element("privatePhobos", toElem(privatePhobos)); elem ~= new xml.Element("additionalOptions", toElem(additionalOptions)); elem ~= new xml.Element("preBuildCommand", toElem(preBuildCommand)); @@ -1325,6 +1348,7 @@ class ProjectOptions fromElem(elem, "exefile", exefile); fromElem(elem, "useStdLibPath", useStdLibPath); fromElem(elem, "cRuntime", cRuntime); + fromElem(elem, "privatePhobos", privatePhobos); fromElem(elem, "additionalOptions", additionalOptions); fromElem(elem, "preBuildCommand", preBuildCommand); @@ -1596,7 +1620,7 @@ class ConfigProvider : DisposingComObject, foreach(c; mConfigs) if(c.mName == strOldName) - config.mName = strNewName; + c.mName = strNewName; NotifyConfigEvent(delegate (IVsCfgProviderEvents cb) { cb.OnCfgNameRenamed(pszOldName, pszNewName); }); @@ -2294,9 +2318,9 @@ class Config : DisposingComObject, switch(mProjectOptions.compiler) { default: - case 0: return mProjectOptions.isX86_64 ? "cl" : "dmc"; - case 1: return "gcc"; - case 2: return "clang"; + case Compiler.DMD: return mProjectOptions.mscoff || mProjectOptions.isX86_64 ? "cl" : "dmc"; + case Compiler.GDC: return "gcc"; + case Compiler.LDC: return "clang"; } } @@ -2575,7 +2599,7 @@ class Config : DisposingComObject, } if(tool == kToolCpp) { - cmd = mProjectOptions.getCppCommandLine(outfile); + cmd = mProjectOptions.getCppCommandLine(outfile, true); string addOpts = file.GetAdditionalOptions(getCfgName()); if(addOpts.length) cmd ~= " " ~ addOpts; @@ -2671,7 +2695,7 @@ class Config : DisposingComObject, else if(mProjectOptions.compiler == Compiler.LDC) cmd ~= "set LIB=" ~ lpath ~ "\n"; } - if(mProjectOptions.isX86_64) + if(x64 || mscoff) { if(globOpt.WindowsSdkDir.length) cmd ~= "set WindowsSdkDir=" ~ globOpt.WindowsSdkDir ~ "\n"; @@ -2844,6 +2868,149 @@ class Config : DisposingComObject, return files; } + string GetPhobosPath() + { + string libpath = normalizeDir(GetIntermediateDir()); + string libfile = "privatephobos.lib"; + return libpath ~ libfile; + } + + string GetPhobosCommandLine() + { + string libpath = normalizeDir(GetIntermediateDir()); + + bool x64 = mProjectOptions.isX86_64; + bool mscoff = mProjectOptions.compiler == Compiler.DMD && mProjectOptions.mscoff; + string model = "32"; + if(x64) + model = "64"; + else if (mscoff) + model = "32mscoff"; + + string libfile = "privatephobos.lib"; + string lib = libpath ~ libfile; + + string cmdfile = libpath ~ "buildphobos.bat"; + string dmddir = Package.GetGlobalOptions().findDmdBinDir(); + string dmdpath = dmddir ~ "dmd.exe"; + string installDir = normalizeDir(Package.GetGlobalOptions().DMD.InstallDir); + + if(!std.file.exists(dmdpath)) + return "echo dmd.exe not found in DMDInstallDir=" ~ installDir ~ " or through PATH\nexit /B 1"; + + string druntimePath = "src\\druntime\\src\\"; + if(!std.file.exists(installDir ~ druntimePath ~ "object_.d")) + druntimePath = "druntime\\src\\"; + if(!std.file.exists(installDir ~ druntimePath ~ "object_.d")) + return "echo druntime source not found in DMDInstallDir=" ~ installDir ~ "\nexit /B 1"; + + string phobosPath = "src\\phobos\\"; + if(!std.file.exists(installDir ~ phobosPath ~ "std")) + phobosPath = "phobos\\"; + if(!std.file.exists(installDir ~ phobosPath ~ "std")) + return "echo phobos source not found in DMDInstallDir=" ~ installDir ~ "\nexit /B 1"; + + string cmdline = "@echo off\n"; + cmdline ~= "echo Building " ~ lib ~ "\n"; + cmdline ~= getEnvironmentChanges(); + + string opts = " -lib -d " ~ mProjectOptions.dmdCommonCompileOptions(); + + // collect C files + string[] cfiles; + cfiles ~= findDRuntimeFiles(installDir, druntimePath ~ "core", true, true, true); + cfiles ~= findDRuntimeFiles(installDir, phobosPath ~ "etc\\c", true, true, true); + if (cfiles.length) + { + foreach(i, ref file; cfiles) + { + file = installDir ~ file; + string outfile = libpath ~ "phobos-" ~ baseName(file) ~ ".obj"; + string cccmd = mProjectOptions.getCppCommandLine(outfile, i == 0); + cmdline ~= cccmd ~ " -DNO_snprintf " ~ file ~ "\n"; + cmdline ~= "if errorlevel 1 exit /B %ERRORLEVEL%\n\n"; + file = outfile; + } + } + + // collect druntime D files + string[] files; + files ~= druntimePath ~ "object_.d"; + files ~= findDRuntimeFiles(installDir, druntimePath ~ "rt", true, false, true); + files ~= findDRuntimeFiles(installDir, druntimePath ~ "core", true, false, true); + files ~= findDRuntimeFiles(installDir, druntimePath ~ "gc", true, false, true); + foreach(ref file; files) + file = installDir ~ file; + files ~= cfiles; + if(model == "32") + files ~= installDir ~ druntimePath ~ "rt\\minit.obj"; + + string dmd; + if(mProjectOptions.otherDMD && mProjectOptions.program.length) + dmd = quoteNormalizeFilename(mProjectOptions.program); + else + dmd = "dmd"; + + static string buildFiles(string dmd, string outlib, string[] files) + { + string rspfile = outlib ~ ".rsp"; + string qrspfile = quoteFilename(rspfile); + string cmdline = "echo. >" ~ qrspfile ~ "\n"; + foreach(file; files) + cmdline ~= "echo " ~ quoteFilename(file) ~ " >>" ~ qrspfile ~ "\n"; + cmdline ~= dmd ~ " -of" ~ quoteFilename(outlib) ~ " @" ~ qrspfile ~ "\n\n"; + return cmdline; + } + + // because of inconsistent object.di and object_.d in dmd <2.067 we have to build + // druntime and phobos seperately + + string druntimelib = libpath ~ "privatedruntime.lib"; + cmdline ~= buildFiles(dmd ~ opts, druntimelib, files); + cmdline ~= "if errorlevel 1 exit /B %ERRORLEVEL%\n\n"; + + // collect phobos D files + files = null; + files ~= findDRuntimeFiles(installDir, phobosPath ~ "std", true, false, true); + files ~= findDRuntimeFiles(installDir, phobosPath ~ "etc\\c", true, false, true); + foreach(ref file; files) + file = installDir ~ file; + + cmdline ~= buildFiles(dmd ~ opts ~ " " ~ quoteFilename(druntimelib), lib, files); + + cmdline = mProjectOptions.replaceEnvironment(cmdline, this, null, lib); + + return cmdline; + } + + bool isPhobosUptodate(string* preason) + { + string workdir = GetProjectDir(); + string outfile = GetPhobosPath(); + string lib = makeFilenameAbsolute(outfile, workdir); + if (!std.file.exists(lib)) + { + if(preason) + *preason = "does not exist"; + return false; + } + string cmd = GetPhobosCommandLine(); + if(cmd.length == 0) + return true; + + string cmdfile = makeFilenameAbsolute(outfile ~ "." ~ kCmdLogFileExtension, workdir); + if(!compareCommandFile(cmdfile, cmd)) + { + if(preason) + *preason = "command line has changed"; + return false; + } + + // no further dependency checks + return true; + } + + string getCommandLine() { bool doLink = mProjectOptions.compilationModel != ProjectOptions.kSeparateCompileOnly; diff --git a/visuald/dpackage.d b/visuald/dpackage.d index 0c496c0e..83a4d729 100644 --- a/visuald/dpackage.d +++ b/visuald/dpackage.d @@ -32,6 +32,7 @@ import visuald.stringutil; import visuald.fileutil; import visuald.dproject; import visuald.automation; +import visuald.build; import visuald.config; import visuald.chiernode; import visuald.dlangsvc; @@ -1769,43 +1770,6 @@ class GlobalOptions return null; } - string[] findDFiles(string path, string sub, bool deep) - { - string[] files; - if(!isExistingDir(path ~ sub)) - return files; - foreach(string file; dirEntries(path ~ sub, SpanMode.shallow)) - { - if(_startsWith(file, path)) - file = file[path.length .. $]; - if (deep && isExistingDir(path ~ file)) - { - string[] exclude = [ "\\internal", "\\freebsd", "\\linux", "\\osx", "\\posix", "\\solaris" ]; - if (!any!(e => file.endsWith(e))(exclude)) - files ~= findDFiles(path, file, deep); - continue; - } - string bname = baseName(file); - if(globMatch(bname, "openrj.d")) - continue; - if(globMatch(bname, "*.d")) - if(string* pfile = contains(files, file ~ "i")) - *pfile = file; - else - files ~= file; - else if(globMatch(bname, "*.di")) - { - // use the d file instead if available - string dfile = "..\\src\\" ~ file[0..$-1]; - if(std.file.exists(path ~ dfile)) - file = dfile; - if(!contains(files, file[0..$-1])) - files ~= file; - } - } - return files; - } - bool buildPhobosBrowseInfo() { IVsOutputWindowPane pane = getBuildOutputPane(); @@ -1837,9 +1801,7 @@ class GlobalOptions } catch(Exception) { - msg = "cannot create directory " ~ jsonPath; - pane.OutputString(toUTF16z(msg)); - return false; + return OutputErrorString(msg = "cannot create directory " ~ jsonPath); } } @@ -1851,11 +1813,8 @@ class GlobalOptions string dmddir = findDmdBinDir(); string dmdpath = dmddir ~ "dmd.exe"; if(!std.file.exists(dmdpath)) - { - msg = "dmd.exe not found in DMDInstallDir=" ~ DMD.InstallDir ~ " or through PATH"; - pane.OutputString(toUTF16z(msg)); - return false; - } + return OutputErrorString(msg = "dmd.exe not found in DMDInstallDir=" ~ DMD.InstallDir ~ " or through PATH"); + foreach(s; imports) { string[] files; @@ -1865,24 +1824,24 @@ class GlobalOptions if(std.file.exists(s ~ "std\\algorithm.d")) // D2 { - files ~= findDFiles(s, "std", true); - files ~= findDFiles(s, "etc\\c", true); + files ~= findDRuntimeFiles(s, "std", true); + files ~= findDRuntimeFiles(s, "etc\\c", true); jsonfile = jsonPath ~ "phobos.json"; } if(std.file.exists(s ~ "std\\gc.d")) // D1 { - files ~= findDFiles(s, "std", false); - files ~= findDFiles(s, "std\\c", false); - files ~= findDFiles(s, "std\\c\\windows", false); - files ~= findDFiles(s, "std\\windows", false); + files ~= findDRuntimeFiles(s, "std", false); + files ~= findDRuntimeFiles(s, "std\\c", false); + files ~= findDRuntimeFiles(s, "std\\c\\windows", false); + files ~= findDRuntimeFiles(s, "std\\windows", false); jsonfile = jsonPath ~ "phobos1.json"; } if(std.file.exists(s ~ "object.di")) { opts ~= " -I" ~ buildPath(s, "..\\src"); // needed since dmd 2.059 files ~= "object.di"; - files ~= findDFiles(s, "core", true); - files ~= findDFiles(s, "std", false); // D1? + files ~= findDRuntimeFiles(s, "core", true); + files ~= findDRuntimeFiles(s, "std", false); // D1? jsonfile = jsonPath ~ "druntime.json"; } @@ -1891,62 +1850,15 @@ class GlobalOptions string sfiles = std.string.join(files, " "); cmdline ~= quoteFilename(dmdpath) ~ opts ~ " -Xf" ~ quoteFilename(jsonfile) ~ " " ~ sfiles ~ "\n\n"; pane.OutputString(toUTF16z("Building " ~ jsonfile ~ " from import " ~ s ~ "\n")); - if(!launchBuildPhobosBrowseInfo(s, cmdfile, cmdline, pane)) + if(!launchBuildPhobos(s, cmdfile, cmdline, pane)) pane.OutputString(toUTF16z("Building " ~ jsonfile ~ " failed!\n")); + else + pane.OutputString(toUTF16z("Building " ~ jsonfile ~ " successful!\n")); } } return true; } - bool launchBuildPhobosBrowseInfo(string workdir, string cmdfile, string cmdline, IVsOutputWindowPane pane) - { - mixin(LogCallMix); - - ///////////// - auto srpIVsLaunchPadFactory = queryService!(IVsLaunchPadFactory); - if (!srpIVsLaunchPadFactory) - return false; - scope(exit) release(srpIVsLaunchPadFactory); - - ComPtr!(IVsLaunchPad) srpIVsLaunchPad; - HRESULT hr = srpIVsLaunchPadFactory.CreateLaunchPad(&srpIVsLaunchPad.ptr); - if(FAILED(hr) || !srpIVsLaunchPad.ptr) - { - string msg = format("internal error: IVsLaunchPadFactory.CreateLaunchPad failed with rc=%x", hr); - pane.OutputString(toUTF16z(msg)); - return false; - } - - try - { - std.file.write(cmdfile, cmdline); - } - catch(FileException e) - { - string msg = format("internal error: cannot write file " ~ cmdfile ~ "\n"); - pane.OutputString(toUTF16z(msg)); - return false; - } - scope(exit) std.file.remove(cmdfile); - - BSTR bstrOutput; - DWORD result; - hr = srpIVsLaunchPad.ExecCommand( - /* [in] LPCOLESTR pszApplicationName */ _toUTF16z(getCmdPath()), - /* [in] LPCOLESTR pszCommandLine */ _toUTF16z("/Q /C " ~ quoteFilename(cmdfile)), - /* [in] LPCOLESTR pszWorkingDir */ _toUTF16z(workdir), // may be NULL, passed on to CreateProcess (wee Win32 API for details) - /* [in] LAUNCHPAD_FLAGS lpf */ LPF_PipeStdoutToOutputWindow, - /* [in] IVsOutputWindowPane *pOutputWindowPane */ pane, // if LPF_PipeStdoutToOutputWindow, which pane in the output window should the output be piped to - /* [in] ULONG nTaskItemCategory */ 0, // if LPF_PipeStdoutToTaskList is specified - /* [in] ULONG nTaskItemBitmap */ 0, // if LPF_PipeStdoutToTaskList is specified - /* [in] LPCOLESTR pszTaskListSubcategory */ null, // "Build"w.ptr, // if LPF_PipeStdoutToTaskList is specified - /* [in] IVsLaunchPadEvents *pVsLaunchPadEvents */ null, //pLaunchPadEvents, - /* [out] DWORD *pdwProcessExitCode */ &result, - /* [out] BSTR *pbstrOutput */ &bstrOutput); // all output generated (may be NULL) - - return hr == S_OK && result == 0; - } - string findCoverageFile(string srcfile) { import stdext.path; diff --git a/visuald/fileutil.d b/visuald/fileutil.d index 8f4e6c13..4b75459e 100644 --- a/visuald/fileutil.d +++ b/visuald/fileutil.d @@ -10,8 +10,12 @@ module visuald.fileutil; import visuald.windows; +import stdext.array; +import stdext.file; import stdext.path; +import stdext.string; +import std.algorithm; import std.path; import std.file; import std.string; @@ -144,3 +148,51 @@ string shortFilename(string fname) return ""; return to!string(sptr[0..len]); } + +string[] findDRuntimeFiles(string path, string sub, bool deep, bool cfiles = false, bool internals = false) +{ + string[] files; + if(!isExistingDir(path ~ sub)) + return files; + foreach(string file; dirEntries(path ~ sub, SpanMode.shallow)) + { + if(_startsWith(file, path)) + file = file[path.length .. $]; + if (deep && isExistingDir(path ~ file)) + { + string[] exclude = [ "\\internal", "\\freebsd", "\\linux", "\\osx", "\\posix", "\\solaris" ]; + if (internals) + exclude = exclude[1..$]; + if (!any!(e => file.endsWith(e))(exclude)) + files ~= findDRuntimeFiles(path, file, deep, cfiles); + continue; + } + string bname = baseName(file); + if(globMatch(bname, "openrj.d")) + continue; + if(globMatch(bname, "minigzip.c") || globMatch(bname, "example.c")) + continue; + if(cfiles) + { + if(globMatch(bname, "*.c")) + if(!contains(files, file)) + files ~= file; + } + else if(globMatch(bname, "*.d")) + if(string* pfile = contains(files, file ~ "i")) + *pfile = file; + else + files ~= file; + else if(globMatch(bname, "*.di")) + { + // use the d file instead if available + string dfile = "..\\src\\" ~ file[0..$-1]; + if(std.file.exists(path ~ dfile)) + file = dfile; + if(!contains(files, file[0..$-1])) + files ~= file; + } + } + return files; +} + diff --git a/visuald/pkgutil.d b/visuald/pkgutil.d index aa763980..0c572a54 100644 --- a/visuald/pkgutil.d +++ b/visuald/pkgutil.d @@ -14,6 +14,7 @@ import visuald.logutil; import visuald.dpackage; import std.conv; +import std.utf; import sdk.vsi.vsshell; void showStatusBarText(wstring txt) @@ -116,6 +117,16 @@ void writeToBuildOutputPane(string msg) OutputPaneBuffer.push(msg); } +bool OutputErrorString(string msg) +{ + if (IVsOutputWindowPane pane = getBuildOutputPane()) + { + scope(exit) release(pane); + pane.OutputString(toUTF16z(msg)); + } + return false; +} + bool tryWithExceptionToBuildOutputPane(T)(T dg, string errInfo = "") { try diff --git a/visuald/profiler.d b/visuald/profiler.d index 5b3db051..13fc1ab3 100644 --- a/visuald/profiler.d +++ b/visuald/profiler.d @@ -588,6 +588,16 @@ private: else // ensure patched runtime in release enum hasTypeArg = true; + static bool isDSymbolChar(char c) + { + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_') + return true; + return (0x80 & c) != 0; // any compressed or unicode symbol + } + for (size_t i = 0; i < txt.length; i++) + if (!isDSymbolChar(txt[i])) + return txt; + txt = decodeDmdString(txt, p); if(txt.length > 2 && txt[0] == '_' && txt[1] == 'D') { diff --git a/visuald/propertypage.d b/visuald/propertypage.d index c9dd5d2c..8442ffc6 100644 --- a/visuald/propertypage.d +++ b/visuald/propertypage.d @@ -1221,6 +1221,11 @@ class DmdLinkerPropertyPage : ProjectPropertyPage override string GetCategoryName() { return "Linker"; } override string GetPageName() { return "General"; } + this() + { + kNeededLines = 11; + } + override void UpdateDirty(bool bDirty) { super.UpdateDirty(bDirty); @@ -1239,6 +1244,7 @@ class DmdLinkerPropertyPage : ProjectPropertyPage AddControl("Generate Map File", mGenMap = new ComboBox(mCanvas, [ "Minimum", "Symbols By Address", "Standard", "Full", "With cross references" ], false)); AddControl("", mImplib = new CheckBox(mCanvas, "Create import library")); + AddControl("", mPrivatePhobos = new CheckBox(mCanvas, "Build and use local version of phobos with same compiler options")); AddControl("", mUseStdLibPath = new CheckBox(mCanvas, "Use global and standard library search paths")); AddControl("C Runtime", mCRuntime = new ComboBox(mCanvas, [ "None", "Static Release (LIBCMT)", "Static Debug (LIBCMTD)", "Dynamic Release (MSCVRT)", "Dynamic Debug (MSCVRTD)" ], false)); } @@ -1246,7 +1252,7 @@ class DmdLinkerPropertyPage : ProjectPropertyPage void EnableControls() { if(ProjectOptions options = GetProjectOptions()) - mCRuntime.setEnabled(options.isX86_64); + mCRuntime.setEnabled(options.isX86_64 || options.mscoff); } override void SetControls(ProjectOptions options) @@ -1260,6 +1266,7 @@ class DmdLinkerPropertyPage : ProjectPropertyPage mGenMap.setSelection(options.mapverbosity); mImplib.setChecked(options.createImplib); mUseStdLibPath.setChecked(options.useStdLibPath); + mPrivatePhobos.setChecked(options.privatePhobos); mCRuntime.setSelection(options.cRuntime); EnableControls(); @@ -1277,6 +1284,7 @@ class DmdLinkerPropertyPage : ProjectPropertyPage changes += changeOption(cast(uint) mGenMap.getSelection(), options.mapverbosity, refoptions.mapverbosity); changes += changeOption(mImplib.isChecked(), options.createImplib, refoptions.createImplib); changes += changeOption(mUseStdLibPath.isChecked(), options.useStdLibPath, refoptions.useStdLibPath); + changes += changeOption(mPrivatePhobos.isChecked(), options.privatePhobos, refoptions.privatePhobos); changes += changeOption(cast(uint) mCRuntime.getSelection(), options.cRuntime, refoptions.cRuntime); return changes; } @@ -1290,6 +1298,7 @@ class DmdLinkerPropertyPage : ProjectPropertyPage ComboBox mGenMap; CheckBox mImplib; CheckBox mUseStdLibPath; + CheckBox mPrivatePhobos; ComboBox mCRuntime; }