private static int Main(string[] args) { #if DEBUG LogManager.AddDebugListener(true); #endif var consoleLogListener = new OutputLogListener(); LogManager.AddListener(consoleLogListener); Uri remoteGitUrl = null; string commitId = null; string baseDir = null; string pdbPath = null; bool skipVerify = false; bool allDepotFiles = false; LinkMethod method = LinkMethod.Http; var arguments = ArgumentSyntax.Parse(args, syntax => { syntax.DefineOption("m|method", ref method, v => (LinkMethod)Enum.Parse(typeof(LinkMethod), v, true), "The method for SRCSRV to retrieve source code. One of <" + string.Join("|", Enum.GetNames(typeof(LinkMethod))) + ">. Default is " + method + "."); syntax.DefineOption("u|url", ref remoteGitUrl, s => new Uri(s, UriKind.Absolute), "Url to remote git repository."); syntax.DefineOption("commit", ref commitId, "The git ref to assume all the source code belongs to."); syntax.DefineOption("baseDir", ref baseDir, "The path to the root of the git repo."); syntax.DefineOption("s|skipVerify", ref skipVerify, "Verify all source files are available in source control."); syntax.DefineOption("a|allDepotFiles", ref allDepotFiles, "Index all source files from depot. Add this option for native PDBs (C++)."); syntax.DefineParameter("pdb", ref pdbPath, "The PDB to add source indexing to."); if (!string.IsNullOrEmpty(pdbPath) && !File.Exists(pdbPath)) { syntax.ReportError($"File not found: \"{pdbPath}\""); } if (!string.IsNullOrEmpty(baseDir) && !Directory.Exists(baseDir)) { syntax.ReportError($"Directory not found: \"{baseDir}\""); } }); if (string.IsNullOrEmpty(pdbPath)) { Log.Info(arguments.GetHelpText()); return(1); } var options = new LinkOptions { GitRemoteUrl = remoteGitUrl, GitWorkingDirectory = baseDir != null?Catel.IO.Path.GetFullPath(baseDir, Environment.CurrentDirectory) : null, CommitId = commitId, SkipVerify = skipVerify, Method = method, IndexAllDepotFiles = allDepotFiles, }; if (!Linker.Link(pdbPath, options)) { return(1); } WaitForKeyPressWhenDebugging(); return(0); }
public static bool Link(string pdbPath, LinkOptions options = default(LinkOptions)) { Argument.IsNotNullOrEmpty(() => pdbPath); var projectSrcSrvFile = pdbPath + ".srcsrv"; string repositoryDirectory; IReadOnlyCollection <string> sourceFiles; IReadOnlyDictionary <string, string> repoSourceFiles; using (var pdb = new PdbFile(pdbPath)) { sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); if (options.GitWorkingDirectory != null) { repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); } else { string someSourceFile = sourceFiles.FirstOrDefault(); if (someSourceFile == null) { Log.Error("No source files were found in the PDB."); return(false); } repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(someSourceFile)); if (repositoryDirectory == null) { Log.Error("No source files found that are tracked in a git repo."); return(false); } } string workingDirectory = Path.GetDirectoryName(repositoryDirectory); var repository = new Lazy <Repository>(() => new Repository(repositoryDirectory)); try { string commitId = options.CommitId ?? repository.Value.Head.Commits.FirstOrDefault()?.Sha; if (commitId == null) { Log.Error("No commit is checked out to HEAD. Have you committed yet?"); return(false); } var providerManager = new Providers.ProviderManager(); Providers.IProvider provider; if (options.GitRemoteUrl == null) { var candidateProviders = from remote in repository.Value.Network.Remotes let p = providerManager.GetProvider(remote.Url) where p != null select p; provider = candidateProviders.FirstOrDefault(); } else { provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); } if (provider == null) { Log.Error("Unable to detect the remote git service."); return(false); } try { Repository repo = repository.Value; repoSourceFiles = sourceFiles.ToDictionary(e => e, e => repo.GetNormalizedPath(e)); } catch (RepositoryNotFoundException) { // Normalize using file system since we can't find the git repo. Log.Warning($"Unable to find git repo at \"{options.GitWorkingDirectory}\". Using file system to find canonical capitalization of file paths."); repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, workingDirectory)); } if (!options.SkipVerify) { Log.Debug("Verifying pdb file"); var missingFiles = pdb.FindMissingOrChangedSourceFiles(); foreach (var missingFile in missingFiles) { Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); } } string rawUrl = provider.RawGitUrl; if (rawUrl.Contains(RevisionPlaceholder) || rawUrl.Contains(FilenamePlaceholder)) { if (!rawUrl.Contains(RevisionPlaceholder) || !rawUrl.Contains(FilenamePlaceholder)) { Log.Error("Supplied custom URL pattern must contain both a revision and a filename placeholder."); return(false); } rawUrl = rawUrl .Replace(RevisionPlaceholder, "{0}") .Replace(FilenamePlaceholder, "%var2%"); } else { rawUrl = $"{rawUrl}/{{0}}/%var2%"; } Log.Info($"Using {string.Format(rawUrl, commitId)} for source server URLs."); var srcSrvContext = new SrcSrvContext { RawUrl = rawUrl, DownloadWithPowershell = options.Method == LinkMethod.Powershell, Revision = commitId, }; foreach (var sourceFile in repoSourceFiles) { // Skip files that aren't tracked by source control. if (sourceFile.Value != null) { string relativePathForUrl = ReplaceSlashes(provider, sourceFile.Value); srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, relativePathForUrl)); } } // When using the VisualStudioTeamServicesProvider, add extra infomration to dictionary with VSTS-specific data if (provider is Providers.VisualStudioTeamServicesProvider) { srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectUrl; } CreateSrcSrv(projectSrcSrvFile, srcSrvContext); } catch (RepositoryNotFoundException) { Log.Error($"Unable to find git repo at \"{options.GitWorkingDirectory}\"."); return(false); } finally { if (repository.IsValueCreated) { repository.Value.Dispose(); } } } Log.Debug("Created source server link file, updating pdb file \"{0}\"", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); var indexedFilesCount = repoSourceFiles.Values.Count(v => v != null); Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count} files written to pdb: \"{pdbPath}\""); return(true); }