public Compiler(Crosspath path) { ExePath = path; // warning saves reference ShortName = ExePath.LastEntry; Instances = new List <CompilerInstance>(); IncludeDirectories = new List <IncludeDirectory>(); }
public void GetContainingDirectory() { String filename1 = "lzcintrin.h"; Crosspath xpath1 = Crosspath.FromString(filename1); xpath1.ToContainingDirectory(); Assert.AreEqual(".", xpath1.ToString()); String filename2 = "/lzcintrin.h"; Crosspath xpath2 = Crosspath.FromString(filename2); xpath2.ToContainingDirectory(); Assert.AreEqual("/", xpath2.ToString()); String filename3 = "someinc/lzcintrin.h"; Crosspath xpath3 = Crosspath.FromString(filename3); xpath3.ToContainingDirectory(); Assert.AreEqual("someinc", xpath3.ToString()); String filename4 = "/path/to/lzcintrin.h"; Crosspath xpath4 = Crosspath.FromString(filename4); xpath4.ToContainingDirectory(); Assert.AreEqual("/path/to", xpath4.ToString()); }
public void EmptyPathParse() { Crosspath cpath = Crosspath.FromString(@""); Assert.IsInstanceOfType(cpath, typeof(RelativeCrosspath)); Assert.AreEqual(CrosspathFlavor.Unix, cpath.Flavor); Assert.AreEqual(CrosspathOrigin.Relative, cpath.Origin); }
public void HashDefaultEquality() { HashSet <Crosspath> hs = new HashSet <Crosspath>(); hs.Add(Crosspath.FromString("/usr/share/vcxprojwriter")); hs.Add(Crosspath.FromString("/usr/share/vcxprojwriter")); Assert.AreEqual(1, hs.Count); }
public void UnixRoot() { Crosspath cpath = Crosspath.FromString(@"/"); Assert.IsInstanceOfType(cpath, typeof(AbsoluteCrosspath)); Assert.AreEqual(CrosspathFlavor.Unix, cpath.Flavor); Assert.AreEqual(CrosspathOrigin.Absolute, cpath.Origin); Assert.AreEqual(@"/", cpath.ToAbsolutizedString()); }
public void RelativeWithGarbage1() { Crosspath cpath = Crosspath.FromString(@"qemu/src/../inc"); Assert.IsInstanceOfType(cpath, typeof(RelativeCrosspath)); Assert.AreEqual(CrosspathFlavor.Unix, cpath.Flavor); Assert.AreEqual(CrosspathOrigin.Relative, cpath.Origin); Assert.AreEqual(@"qemu/inc", ((RelativeCrosspath)cpath).ToString()); }
public void RebaseFromUnixToWindowsTestFull() { Crosspath xpath = Crosspath.FromString("/local/store/bin-src/qemu"); AbsoluteCrosspath before = Crosspath.FromString("/local/store/bin-src/qemu") as AbsoluteCrosspath; AbsoluteCrosspath after = Crosspath.FromString(@"D:\Workspace\Source\qemu") as AbsoluteCrosspath; AbsoluteCrosspath apath = ((AbsoluteCrosspath)xpath).Rebase(before, after); Assert.AreEqual(CrosspathFlavor.Windows, apath.Flavor); Assert.AreEqual('D', apath.WindowsRootDrive); Assert.AreEqual(@"D:\Workspace\Source\qemu", apath.ToString()); Assert.AreEqual(@"D:\Workspace\Source\qemu", apath.ToAbsolutizedString()); }
public void TestMethod3() { Crosspath cpath = Crosspath.FromString(@"/local/store/qemu"); Assert.IsInstanceOfType(cpath, typeof(AbsoluteCrosspath)); Assert.AreEqual(CrosspathFlavor.Unix, cpath.Flavor); Assert.AreEqual(CrosspathOrigin.Absolute, cpath.Origin); Assert.AreEqual(@"/local/store/qemu", ((AbsoluteCrosspath)cpath).ToString()); // DONE: substitute (rebase) absolute paths Assert.AreEqual(@"D:\Projects\local\store\qemu" , ((AbsoluteCrosspath)cpath).Rebase(AbsoluteCrosspath.FromString("/") , AbsoluteCrosspath.FromString(@"D:\Projects")).ToString()); }
public void EmptyPathProcess() { Crosspath cpath = Crosspath.FromString(@""); Assert.IsInstanceOfType(cpath, typeof(RelativeCrosspath)); Assert.AreEqual(CrosspathFlavor.Unix, cpath.Flavor); Assert.AreEqual(CrosspathOrigin.Relative, cpath.Origin); try { cpath.ToAbsolutizedString(); Assert.Fail("should fail"); } catch (PolymorphismException) { } }
public void HashDiversedEquality() { HashSet <Crosspath> hs = new HashSet <Crosspath> { Crosspath.FromString("/tmp/VCXProjWriter") , AbsoluteCrosspath.FromString("/tmp/VCXProjWriter1") , RelativeCrosspath.FromString("tmp/VCXProjWriter") }; RelativeCrosspath relativeCrosspath = RelativeCrosspath.FromString("VCXProjWriter"); relativeCrosspath.SetWorkingDirectory(AbsoluteCrosspath.FromString("/tmp")); Boolean has_added = hs.Add(relativeCrosspath); Assert.AreEqual(false, has_added); Assert.AreEqual(3, hs.Count); }
public void TryCompileDBEntry() { String file = "source.c"; String directory = "/path/to/file"; // from Solution.cs Crosspath xpath = Crosspath.FromString(file); Crosspath xdir = Crosspath.FromString(directory); if (xpath is RelativeCrosspath relPath) { relPath.SetWorkingDirectory(xdir as AbsoluteCrosspath); } Assert.IsInstanceOfType(xpath, typeof(RelativeCrosspath)); Assert.AreEqual(CrosspathOrigin.Relative, xpath.Origin); Assert.AreEqual(CrosspathFlavor.Unix, xpath.Flavor); Assert.IsInstanceOfType(xdir, typeof(AbsoluteCrosspath)); Assert.AreEqual(CrosspathOrigin.Absolute, xdir.Origin); Assert.AreEqual(CrosspathFlavor.Unix, xdir.Flavor); Assert.AreEqual(@"/path/to/file/source.c", ((RelativeCrosspath)xpath).Absolutized().ToString()); }
public void DynamicReturn() { Crosspath cpath = Crosspath.FromString(@"/"); if (cpath.Origin == CrosspathOrigin.Absolute) { Logger.LogMessage(((AbsoluteCrosspath)cpath).ToString()); } else { Assert.Fail("it was absolute"); } Crosspath cpath2 = Crosspath.FromString(@"."); if (cpath2.Origin == CrosspathOrigin.Relative) { Logger.LogMessage(((RelativeCrosspath)cpath2).ToString()); } else { Assert.Fail("it was relative"); } }
public void Polymorphism() { Crosspath cpath = Crosspath.FromString(@"/"); AbsoluteCrosspath unused = cpath as AbsoluteCrosspath; RelativeCrosspath unused1 = cpath as RelativeCrosspath; }
/// <summary> /// Call RemoteHost.ExtractInfoFromCompiler() instead. /// Requires these utilities on the remote system: pwd, echo, which, touch, sort, pushd, popd, zip /// </summary> /// <param name="remote">Remote host where compiler installed</param> public void ExtractAdditionalInfo(RemoteHost remote) { String remoteTempFile = String.Format(Compiler.RemoteTempFile, BaseCompiler.ShortName, instanceGuid.ToString()); if (remote.Execute("pwd || echo $PWD", out String pwd) != RemoteHost.Success) { throw new ApplicationException("could not get current directory name"); } pwd = pwd.TrimEnd(RemoteHost.LineEndingChars); if (remote.Execute($"which {BaseCompiler.ExePath}", out String absExePath) != RemoteHost.Success) { throw new ApplicationException("could not get absolute compiler path"); } absExePath = absExePath.TrimEnd(RemoteHost.LineEndingChars); if (remote.Execute($"{BaseCompiler.ExePath} -dumpversion", out String version) != RemoteHost.Success) { throw new ApplicationException("could not extract version from compiler"); } Version = version.TrimEnd(RemoteHost.LineEndingChars); // create temporary .c file for auto-distinguishment between C and C++ if (remote.Execute($"touch {remoteTempFile} || echo > {remoteTempFile}", out String _) != RemoteHost.Success) { throw new ApplicationException("could not create temporary file"); } if (remote.Execute($"{BaseCompiler.ExePath} {this} -E -dM {remoteTempFile} | sort", out String defines) != RemoteHost.Success) { throw new ApplicationException("could not extract defines from compiler"); } if (remote.Execute($"{BaseCompiler.ExePath} {this} -E -Wp,-v {remoteTempFile} 2>&1 1> /dev/null", out String includeDirs) != RemoteHost.Success) { throw new ApplicationException("could not extract include dirs from compiler"); } AbsoluteCrosspath xpwd = AbsoluteCrosspath.FromString(pwd); AbsoluteCrosspath xcompiler = AbsoluteCrosspath.FromString(absExePath); if (!xcompiler.ToString().Equals(BaseCompiler.ExePath.ToString())) { AbsoluteCrosspath compilerLocatedIn = new AbsoluteCrosspath(xcompiler); ((RelativeCrosspath)BaseCompiler.ExePath).SetWorkingDirectory(xcompiler.ToContainingDirectory()); Logger.WriteLine(LogLevel.Info, $"compiler '{BaseCompiler.ExePath}' actually located at '{xcompiler}'"); } Defines.Clear(); Platform fallbackPlatform = Platform.x64; foreach (String macro in defines.Split(RemoteHost.LineEndings, StringSplitOptions.RemoveEmptyEntries)) { // assuming standard format '#define MACRO some thing probably with spaces' String[] defArray = macro.Split(new[] { ' ' }, 3); // try to auto-detect the platform if (VSPlatform == Platform.Unknown) { switch (defArray[1]) { case "__x86_64__": VSPlatform = Platform.x64; break; case "__i386__": VSPlatform = Platform.x86; break; case "__arm__": VSPlatform = Platform.ARM; break; case "__aarch64__": VSPlatform = Platform.ARM64; break; case "__mips__": VSPlatform = Platform.MIPS; break; case "__WORDSIZE__": /* this seems to be standard */ case "__INTPTR_WIDTH__": /* this not, but use as fallback */ if (Int32.Parse(defArray[2]) == 32) { fallbackPlatform = Platform.x86; } break; } } Defines.Add(new Define(defArray[1], defArray[2])); } if (VSPlatform == Platform.Unknown) { VSPlatform = fallbackPlatform; } IncludeDirectories.Clear(); IncludeDirectoryType incDirType = IncludeDirectoryType.Null; foreach (String cppLine in includeDirs.Split(RemoteHost.LineEndings, StringSplitOptions.RemoveEmptyEntries)) { // sample output: /* * ignoring nonexistent directory "/opt/gcc-4.1.2-glibc-2.5-binutils-2.17-kernel-2.6.18/arm-v5te-linux-gnueabi/include" #include "..." search starts here: #include <...> search starts here: * /opt/gcc-4.1.2-glibc-2.5-binutils-2.17-kernel-2.6.18/bin/../lib/gcc/arm-v5te-linux-gnueabi/4.1.2/include * /opt/gcc-4.1.2-glibc-2.5-binutils-2.17-kernel-2.6.18/bin/../sysroot-arm-v5te-linux-gnueabi/usr/include * End of search list. */ // regarding priority: // -iquote has the highest priority affecting #include "..." not only #include <...> // -I has lower priority than -iquote // -isystem has lower priority but yet higher priority than system-wide headers // then follows the priority of system-wide-headers // -idirafter has the lowest priority possible // example (gcc 9): /* * $ gcc -x c -c -Wp,-v -iquote /proc/1 -I /proc/10 -isystem /proc/12 -I /proc/14 -idirafter /proc/1204 - < /dev/null * ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" * ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/include-fixed" * ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include" #include "..." search starts here: * /proc/1 #include <...> search starts here: * /proc/10 * /proc/14 * /proc/12 * /usr/lib/gcc/x86_64-linux-gnu/9/include * /usr/local/include * /usr/include/x86_64-linux-gnu * /usr/include * /proc/1204 * End of search list. */ // usually gcc does not have any include directories in the #include "..." section. // so the reason for parsing this section is when using some compiler wrapper that puts some paths to -iquote. if (cppLine == "#include \"...\" search starts here:") { incDirType = IncludeDirectoryType.Quote; continue; } if (cppLine == "#include <...> search starts here:") { incDirType = IncludeDirectoryType.System; continue; } if (cppLine.Length > 0 && cppLine[0] == ' ') { Crosspath xpath = Crosspath.FromString(cppLine.Substring(1)); if (xpath is RelativeCrosspath relPath) { relPath.SetWorkingDirectory(xpwd); xpath = relPath.Absolutized(); } IncludeDirectory incDir = new IncludeDirectory(xpath as AbsoluteCrosspath, incDirType); IncludeDirectory incDirCached = BaseCompiler.TrackIncludeDir(incDir); if (incDirCached != null) { incDir = incDirCached; } IncludeDirectories.Add(incDir); continue; } if (cppLine == "End of search list.") { break; } } HaveAdditionalInfo = true; Logger.WriteLine(LogLevel.Info, $"{absExePath} {this} is {VSPlatform} compiler"); }
private static void Main(String[] args) { #if !DEBUG try { #endif ProcessArgs(args); if (config.InputFile == null) { ShowHelp(); } Stopwatch sw = new Stopwatch(); sw.Start(); Solution sln = new Solution(config); sln.ParseCompileDB(config.InputFile); sw.Stop(); Int64 parseAndGroup = sw.ElapsedMilliseconds; sw.Reset(); sw.Start(); if (config.Remote != null) { sln.RetrieveExtraInfoFromRemote(config.Remote); } sw.Stop(); Int64 remoteInfo = sw.ElapsedMilliseconds; sln.FilterOutEntries(); sw.Reset(); sw.Start(); foreach (Tuple <AbsoluteCrosspath, AbsoluteCrosspath> substitution in config.Substitutions) { sln.Rebase(substitution.Item1, substitution.Item2); } sw.Stop(); Int64 rebase = sw.ElapsedMilliseconds; // DONE: also check for: // - project file accessibility // - include dirs accessibility List <IncludeDirectory> remoteNotRebased = new List <IncludeDirectory>(); sln.CheckForTotalRebase(ref remoteNotRebased); try { Directory.Delete(config.Outdir, true); } catch { // ignored } if (config.Remote != null) { sln.DownloadCompilerIncludeDirectoriesFromRemote(config.Remote, config.Outdir); foreach (IncludeDirectory includeDirectory in remoteNotRebased) { Crosspath outdir = Crosspath.FromString(config.Outdir); if (outdir is RelativeCrosspath relativeCrosspath) { outdir = relativeCrosspath.Absolutized(AbsoluteCrosspath.GetCurrentDirectory()); } includeDirectory.RebaseToLocal(config.Remote , ((AbsoluteCrosspath)outdir).Append( RelativeCrosspath.FromString(String.Format(SolutionStructure.RemoteIncludePath , config.Remote.Host)))); } } sw.Reset(); sw.Start(); sln.WriteToDirectory(config.Outdir); sw.Stop(); Int64 write = sw.ElapsedMilliseconds; Console.WriteLine(); Console.WriteLine($"Elapsed time: parseAndGroup = {parseAndGroup} ms, remoteInfo = {remoteInfo} ms" + $", rebase = {rebase} ms, write = {write} ms."); if (config.OpenSolution) { Process.Start(new ProcessStartInfo(Path.Combine(config.Outdir, SolutionStructure.SolutionFilename)) { UseShellExecute = true }); } #if !DEBUG } catch (Exception e) { Console.WriteLine($"[x] {e.Message}"); Console.WriteLine(); Console.WriteLine("Stack trace for debugging purposes:"); Console.WriteLine($"{e.StackTrace}"); Console.WriteLine("Press Enter to continue..."); Console.ReadLine(); } #endif }
private static void ProcessArgs(String[] args) { for (int idx = 0; idx < args.Length; idx++) { switch (args[idx]) { case "-s": case "--substitute-path": String[] subst = TakeArg(args, ref idx).Split(new[] { '=' }, 2); if (subst.Length != 2) { throw new ApplicationException("--substitute-path requires next arg to be like '/path/before=/path/after'"); } AbsoluteCrosspath[] pair = new AbsoluteCrosspath[2]; for (int i = 0; i < 2; ++i) { try { pair[i] = Crosspath.FromString(subst[i]) as AbsoluteCrosspath; } catch (Exception e) { throw new PolymorphismException($"{e.GetType()}: '{subst[i]}' is not a valid absolute path"); } } config.Substitutions.Add(new Tuple <AbsoluteCrosspath, AbsoluteCrosspath>(pair[0], pair[1])); break; case "-b": case "--base-dir": config.BaseDir = AbsoluteCrosspath.FromString(TakeArg(args, ref idx)); break; #if PATH_BASED_FILTERING case "--include": config.IncludeFilesFrom.Add(Crosspath.FromString(TakeArg(args, ref idx))); break; case "--exclude": config.ExcludeFilesFrom.Add(Crosspath.FromString(TakeArg(args, ref idx))); break; #endif case "-o": case "--output-dir": config.Outdir = TakeArg(args, ref idx); break; case "-f": case "--file": config.InputFile = TakeArg(args, ref idx); break; case "-r": case "--remote": RemoteHost remote = RemoteHost.Parse(TakeArg(args, ref idx)); config.AssignRemote(remote); break; case "-D": case "--define": config.OverrideDefines.Add(new Define(TakeArg(args, ref idx))); break; case "--exclude-compiler": config.ExcludeCompilers.Add(Crosspath.FromString(TakeArg(args, ref idx))); break; case "--relax-include-dirs-order": config.RelaxIncludeDirsOrder = true; break; case "--randomize-outdir": config.Outdir = Path.Combine(config.Outdir, new Random().Next().ToString("x8")); break; case "--open-now": config.OpenSolution = true; break; case "--debug": Logger.Level = LogLevel.Debug; break; case "--help": ShowHelp(); return; default: throw new Exception($"Unknown command line parameter '{args[idx]}"); } } }
public void ParseCompileDB(String filename) { // hope compiledb is not so large to eat all the memory String compiledbRaw = File.ReadAllText(filename); List <CompileDBEntry> entries = JsonConvert.DeserializeObject <List <CompileDBEntry> >(compiledbRaw); foreach (CompileDBEntry entry in entries) { // get full file path AbsoluteCrosspath workingDir = AbsoluteCrosspath.FromString(entry.directory); AbsoluteCrosspath xpath = AbsoluteCrosspath.FromString(entry.file, workingDir); // TODO: filter out entries by include and exclude lists // get compiler path // it can be absolute or relative Crosspath compilerPath = Crosspath.FromString(entry.arguments[0]); // TODO: filter out compilers by include and exclude lists Compiler compiler = null; foreach (Compiler solutionCompiler in solutionCompilers) { if (solutionCompiler.ExePath.ToString().Equals(compilerPath.ToString())) { // already registered compiler = solutionCompiler; break; } } if (compiler == null) { compiler = new Compiler(compilerPath); if (solutionCompilers.Add(compiler)) { Logger.WriteLine(LogLevel.Info, $"New compiler '{compiler.ExePath}'"); } } CompilerInstance compilerInstance = null; CompilerInstance compilerInstanceTmp = new CompilerInstance(compiler, entry.arguments); foreach (CompilerInstance compilerInstanceInUse in compiler.Instances) { if (compilerInstanceInUse.Equals(compilerInstanceTmp)) { compilerInstance = compilerInstanceInUse; break; } } if (compilerInstance == null) { compilerInstance = compilerInstanceTmp; compiler.Instances.Add(compilerInstanceTmp); Logger.WriteLine(LogLevel.Info, $"New compiler instance '{compilerInstanceTmp.BaseCompiler.ExePath} {compilerInstanceTmp}'"); } ProjectFile pf = new ProjectFile(this, xpath, compilerInstance); Logger.WriteLine(LogLevel.Trace, $"===== file {xpath} ====="); pf.AddInfoFromCommandLine(workingDir, entry.arguments); // put global override defines silently // TODO: write them to solution-wide props foreach (Define define in config.OverrideDefines) { pf.UnsetCppDefine(define.Name); pf.SetCppDefine(define.ToString()); } // add to project files now? //pf.DumpData(); Int64 projectHash = pf.HashProjectID(); if (!projects.ContainsKey(projectHash)) { projects.Add(projectHash, new Project(AllocateGuid(), projectHash, this, compilerInstance, pf.IncludeDirectories, pf.SetOfDefines, pf.ForceIncludes)); } // add file to project if (!projects[projectHash].TestWhetherProjectFileBelongs(pf)) { throw new ApplicationException( $"[x] Could not add '{pf.FilePath}' to project '{projectHash}' - hash function error"); } if (!projects[projectHash].AddProjectFile(pf)) { //throw new ApplicationException( // $"[x] Could not add '{pf.FilePath}' to project '{projectHash}' - already exists"); Logger.WriteLine(LogLevel.Error, $"[x] Could not add '{pf}' to project '{projectHash}' - already exists"); } TrackFile(pf); } Logger.WriteLine(LogLevel.Info, $"[i] Created a solution of {projects.Count} projects from {solutionFiles.Count} files"); if (Logger.Level == LogLevel.Trace) { foreach (var projectKvp in projects) { Logger.WriteLine(LogLevel.Trace, $"# Project ID {projectKvp.Key}"); foreach (var file in projectKvp.Value.ProjectFiles) { Logger.WriteLine(LogLevel.Trace, $"# > {file.FilePath}"); } using (var enumerator = projectKvp.Value.ProjectFiles.GetEnumerator()) { if (!enumerator.MoveNext()) { break; } if (enumerator.Current != null) { enumerator.Current.DumpData(); } } Logger.WriteLine(LogLevel.Trace, "============================================"); } } }