diff --git a/src/CommandLine/CommandLine.csproj b/src/CommandLine/CommandLine.csproj
index 9deac8729..41f970dc6 100644
--- a/src/CommandLine/CommandLine.csproj
+++ b/src/CommandLine/CommandLine.csproj
@@ -1,202 +1,218 @@
-
-
-
- ..\..\NuGet.ruleset
- true
-
-
- x86
- true
- false
- bin\Debug\
- DEBUG;TRACE
-
-
- x86
- pdbonly
- true
- bin\Release\
-
-
-
- {B34A6632-E627-4B66-8E0A-D2DA3BC96893}
- Exe
- Properties
- NuGet
- NuGet
-
-
-
-
-
-
-
- False
- ..\..\lib\Microsoft.Web.XmlTransform.dll
-
-
-
-
-
-
-
-
-
-
-
- Common\MsBuildProjectUtility.cs
-
-
-
-
-
-
-
-
- True
- True
- HelpCommandMarkdownTemplate.cshtml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Common\NuGetConstants.cs
-
-
-
-
- Properties\CommonAssemblyInfo.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- NuGetCommand.resx
-
-
-
-
- True
- True
- NuGetResources.resx
-
-
-
-
-
-
-
-
-
-
- Common\CommonResources.cs
-
-
-
-
-
- Properties\CodeAnalysisDictionary.xml
- Designer
-
-
-
-
- {F879F274-EFA0-4157-8404-33A19B4E6AEC}
- Core
-
-
-
-
- Common\CommonResources.resx
- CommonResources.cs
- Designer
-
-
- Designer
-
-
- ResXFileCodeGenerator
- NuGetResources.Designer.cs
- Designer
-
-
-
-
-
-
-
- Code
- RazorGenerator
- HelpCommandMarkdownTemplate.cs
-
-
-
-
-
- $(MsBuildProjectDirectory)\..\..
- $(NuGetRoot)\Tools\ILMerge\ILMerge.exe
- $(NuGetRoot)\Build\ilmerge.internalize.ignore.txt
- NuGet.exe
- $(OutputPath)Merged\$(ILMergeOutputFile)
- $(OutputPath)Signed\$(ILMergeOutputFile)
- $(ProgramFiles)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
- $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
- /targetplatform:"v4, $(FrameworkPath)" /internalize:"$(ILMergeInternalizeIgnoreFile)" /target:exe /out:"Merged\$(ILMergeOutputFile)" /log:"Merged\ilmerge.msbuild.log" /allowDup NuGet.exe NuGet.Core.dll Microsoft.Web.XmlTransform.dll
- /targetplatform:"v4, $(FrameworkPath)" /internalize:"$(ILMergeInternalizeIgnoreFile)" /target:exe /out:"Signed\$(ILMergeOutputFile)" /log:"Signed\ilmerge.msbuild.log" /allowDup /keyfile:"$(AssemblyOriginatorKeyFile)" /delaysign NuGet.exe NuGet.Core.dll Microsoft.Web.XmlTransform.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ ..\..\NuGet.ruleset
+ true
+ $(UserProfile)\.nuget\packages
+
+
+ x86
+ true
+ false
+ bin\Debug\
+ DEBUG;TRACE
+
+
+ x86
+ pdbonly
+ true
+ bin\Release\
+
+
+
+ {B34A6632-E627-4B66-8E0A-D2DA3BC96893}
+ Exe
+ Properties
+ NuGet
+ NuGet
+
+
+
+
+
+
+
+ $(NuGetPackageRoot)\Microsoft.DiaSymReader\1.0.6\lib\portable-net45+win8\Microsoft.DiaSymReader.dll
+
+
+ False
+ ..\..\lib\Microsoft.Web.XmlTransform.dll
+
+
+
+
+
+
+
+
+
+
+
+ Common\MsBuildProjectUtility.cs
+
+
+
+
+
+
+
+
+ True
+ True
+ HelpCommandMarkdownTemplate.cshtml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Common\NuGetConstants.cs
+
+
+
+
+ Properties\CommonAssemblyInfo.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NuGetCommand.resx
+
+
+
+
+ True
+ True
+ NuGetResources.resx
+
+
+
+
+
+
+
+
+
+
+ Common\CommonResources.cs
+
+
+
+
+
+ PreserveNewest
+ false
+
+
+ PreserveNewest
+ false
+
+
+
+
+ Properties\CodeAnalysisDictionary.xml
+ Designer
+
+
+
+
+ {F879F274-EFA0-4157-8404-33A19B4E6AEC}
+ Core
+
+
+
+
+ Common\CommonResources.resx
+ CommonResources.cs
+ Designer
+
+
+ Designer
+
+
+ ResXFileCodeGenerator
+ NuGetResources.Designer.cs
+ Designer
+
+
+
+
+
+
+
+
+ Code
+ RazorGenerator
+ HelpCommandMarkdownTemplate.cs
+
+
+
+
+
+ $(MsBuildProjectDirectory)\..\..
+ $(NuGetRoot)\Tools\ILMerge\ILMerge.exe
+ $(NuGetRoot)\Build\ilmerge.internalize.ignore.txt
+ NuGet.exe
+ $(OutputPath)Merged\$(ILMergeOutputFile)
+ $(OutputPath)Signed\$(ILMergeOutputFile)
+ $(ProgramFiles)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
+ $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0
+ /targetplatform:"v4, $(FrameworkPath)" /internalize:"$(ILMergeInternalizeIgnoreFile)" /target:exe /out:"Merged\$(ILMergeOutputFile)" /log:"Merged\ilmerge.msbuild.log" /allowDup NuGet.exe NuGet.Core.dll Microsoft.Web.XmlTransform.dll
+ /targetplatform:"v4, $(FrameworkPath)" /internalize:"$(ILMergeInternalizeIgnoreFile)" /target:exe /out:"Signed\$(ILMergeOutputFile)" /log:"Signed\ilmerge.msbuild.log" /allowDup /keyfile:"$(AssemblyOriginatorKeyFile)" /delaysign NuGet.exe NuGet.Core.dll Microsoft.Web.XmlTransform.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ -->
+
\ No newline at end of file
diff --git a/src/CommandLine/Commands/ProjectFactory.cs b/src/CommandLine/Commands/ProjectFactory.cs
index f6dbcbcda..08c60d6b8 100644
--- a/src/CommandLine/Commands/ProjectFactory.cs
+++ b/src/CommandLine/Commands/ProjectFactory.cs
@@ -173,6 +173,11 @@ public PackageBuilder CreateBuilder(string basePath)
ProcessDependencies(builder);
+ if (IncludeSymbols)
+ {
+ AddMissingSourceFilesReferencedByPdb(builder);
+ }
+
// Set defaults if some required fields are missing
if (String.IsNullOrEmpty(builder.Description))
{
@@ -1093,5 +1098,58 @@ IEnumerable IFrameworkTargetable.SupportedFrameworks
}
}
}
+
+ private void AddMissingSourceFilesReferencedByPdb(PackageBuilder builder)
+ {
+ // We build a new symbol package from physical files, so it's safe to cast here (we need the SourcePath property).
+ var packageFiles = builder.Files.OfType().ToArray();
+
+ var pdbFiles = packageFiles.Where(file => file.Path.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase));
+ var missingFiles = pdbFiles.SelectMany(pdbFile => GetMissingSourceFilesReferencedByPdb(pdbFile, packageFiles));
+
+ foreach (var file in missingFiles)
+ {
+ AddFileToBuilder(builder, file);
+ }
+ }
+
+ private IEnumerable GetMissingSourceFilesReferencedByPdb(IPackageFile pdbFile, IEnumerable packageFiles)
+ {
+ try
+ {
+ var sourceFiles = PdbHelper.GetSourceFileNames(pdbFile);
+ var missingFiles = sourceFiles.Except(packageFiles.Select(file => file.SourcePath), StringComparer.OrdinalIgnoreCase);
+
+ return missingFiles.Select(CreatePackageFileFromSourceFile).Where(file => file != null);
+ }
+ catch (Exception ex)
+ {
+ _logger.Log(MessageLevel.Warning, "{0} seems to be not a valid pdb file ({1})", pdbFile.Path, ex.Message);
+ }
+
+ return Enumerable.Empty();
+ }
+
+ private PhysicalPackageFile CreatePackageFileFromSourceFile(string file)
+ {
+ var targetFilePath = Normalize(file);
+
+ if (!File.Exists(file))
+ {
+ Logger.Log(MessageLevel.Warning, LocalizedResourceManager.GetString("Warning_FileDoesNotExist"), targetFilePath);
+ return null;
+ }
+
+ var projectName = Path.GetFileNameWithoutExtension(_project.FullPath);
+
+ // if IncludeReferencedProjects is true and we are adding source files,
+ // add projectName as part of the target to avoid file conflicts.
+ var targetPath = IncludeReferencedProjects
+ ? Path.Combine(SourcesFolder, projectName, targetFilePath)
+ : Path.Combine(SourcesFolder, targetFilePath);
+
+ return new PhysicalPackageFile { SourcePath = file, TargetPath = targetPath };
+ }
}
}
+
diff --git a/src/CommandLine/Common/PdbHelper.cs b/src/CommandLine/Common/PdbHelper.cs
new file mode 100644
index 000000000..e4f072c50
--- /dev/null
+++ b/src/CommandLine/Common/PdbHelper.cs
@@ -0,0 +1,257 @@
+namespace NuGet.Common
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using System.Runtime.InteropServices.ComTypes;
+ using System.Threading;
+
+ using Microsoft.DiaSymReader;
+
+ using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
+
+ internal static class PdbHelper
+ {
+ public static IEnumerable GetSourceFileNames(IPackageFile pdbFile)
+ {
+ using (var stream = new StreamAdapter(pdbFile.GetStream()))
+ {
+ var reader = SymReaderFactory.CreateNativeSymReader(stream);
+
+ return reader.GetDocuments()
+ .Select(doc => doc.GetName())
+ .Where(IsValidSourceFileName);
+ }
+ }
+
+ private static bool IsValidSourceFileName(string sourceFileName)
+ {
+ return !string.IsNullOrEmpty(sourceFileName) && !IsTemporaryCompilerFile(sourceFileName);
+ }
+
+ private static bool IsTemporaryCompilerFile(string sourceFileName)
+ {
+ //the VB compiler will include temporary files in its pdb files.
+ //the source file name will be similar to 17d14f5c-a337-4978-8281-53493378c1071.vb.
+ return sourceFileName.EndsWith("17d14f5c-a337-4978-8281-53493378c1071.vb", StringComparison.InvariantCultureIgnoreCase);
+ }
+
+ private static class SymReaderFactory
+ {
+ [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
+ [DllImport("Microsoft.DiaSymReader.Native.x86.dll", EntryPoint = "CreateSymReader")]
+ private extern static void CreateSymReader32(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)]out object symReader);
+
+ [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
+ [DllImport("Microsoft.DiaSymReader.Native.amd64.dll", EntryPoint = "CreateSymReader")]
+ private extern static void CreateSymReader64(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)]out object symReader);
+
+ internal static ISymUnmanagedReader3 CreateNativeSymReader(IStream pdbStream)
+ {
+ object symReader = null;
+ var guid = default(Guid);
+
+ if (IntPtr.Size == 4)
+ {
+ CreateSymReader32(ref guid, out symReader);
+ }
+ else
+ {
+ CreateSymReader64(ref guid, out symReader);
+ }
+
+ var reader = (ISymUnmanagedReader3)symReader;
+ var hr = reader.Initialize(new DummyMetadataImport(), null, null, pdbStream);
+ Marshal.ThrowExceptionForHR(hr);
+ return reader;
+ }
+
+ class DummyMetadataImport : IMetadataImport { }
+ }
+
+ ///
+ /// Wrap a Stream so it's usable where we need an IStream
+ ///
+ sealed class StreamAdapter : IStream, IDisposable
+ {
+ Stream _stream;
+ IntPtr _pcbData = Marshal.AllocHGlobal(8); // enough to store long/int64, can be shared since we don't support multithreaded access to one file.
+
+ ///
+ /// Create a new adapter around the given stream.
+ ///
+ /// The stream to wrap.
+ public StreamAdapter(Stream wrappedStream)
+ {
+ _stream = wrappedStream;
+ }
+
+ ~StreamAdapter()
+ {
+ throw new InvalidOperationException("Stream adapter not disposed");
+ }
+
+ public void Clone(out IStream ppstm)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Commit(int grfCommitFlags)
+ {
+ }
+
+ public void LockRegion(long libOffset, long cb, int dwLockType)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Revert()
+ {
+ throw new NotSupportedException();
+ }
+
+ public void UnlockRegion(long libOffset, long cb, int dwLockType)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Read(byte[] pv, int cb, IntPtr pcbRead)
+ {
+ var count = _stream.Read(pv, 0, cb);
+ if (pcbRead != IntPtr.Zero)
+ {
+ Marshal.WriteInt32(pcbRead, count);
+ }
+ }
+
+ public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
+ {
+ var origin = (SeekOrigin)dwOrigin;
+ var pos = _stream.Seek(dlibMove, origin);
+ if (plibNewPosition != IntPtr.Zero)
+ {
+ Marshal.WriteInt64(plibNewPosition, pos);
+ }
+ }
+
+ public void SetSize(long libNewSize)
+ {
+ _stream.SetLength(libNewSize);
+ }
+
+ public void Stat(out STATSTG pstatstg, int grfStatFlag)
+ {
+ pstatstg = new STATSTG
+ {
+ type = 2,
+ cbSize = _stream.Length,
+ grfMode = 0
+ };
+
+ if (_stream.CanRead && _stream.CanWrite)
+ {
+ pstatstg.grfMode |= 2;
+ }
+ else if (_stream.CanWrite && !_stream.CanRead)
+ {
+ pstatstg.grfMode |= 1;
+ }
+ }
+
+ public void Write(byte[] pv, int cb, IntPtr pcbWritten)
+ {
+ _stream.Write(pv, 0, cb);
+ if (pcbWritten != IntPtr.Zero)
+ {
+ Marshal.WriteInt32(pcbWritten, cb);
+ }
+ }
+
+ public void Dispose()
+ {
+ Interlocked.Exchange(ref _stream, null)?.Close();
+
+ var data = Interlocked.Exchange(ref _pcbData, IntPtr.Zero);
+ if (data != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(_pcbData);
+ }
+
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+
+ [ComImport, Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), TypeIdentifier]
+ public interface IMetadataImport { }
+
+ static class SymUnmanagedReaderExtensions
+ {
+ // Excerpt of http://source.roslyn.io/#Roslyn.Test.PdbUtilities/Shared/SymUnmanagedReaderExtensions.cs
+
+ private const int E_FAIL = unchecked((int)0x80004005);
+ private const int E_NOTIMPL = unchecked((int)0x80004001);
+
+ private delegate int ItemsGetter(TEntity entity, int bufferLength, out int count, TItem[] buffer);
+
+ private static string ToString(char[] buffer)
+ {
+ if (buffer.Length == 0)
+ return string.Empty;
+
+ Debug.Assert(buffer[buffer.Length - 1] == 0);
+ return new string(buffer, 0, buffer.Length - 1);
+ }
+
+ private static void ValidateItems(int actualCount, int bufferLength)
+ {
+ if (actualCount != bufferLength)
+ {
+ throw new InvalidOperationException(string.Format("Read only {0} of {1} items.", actualCount, bufferLength));
+ }
+ }
+
+ private static TItem[] GetItems(TEntity entity, ItemsGetter getter)
+ {
+ int count;
+ var hr = getter(entity, 0, out count, null);
+ ThrowExceptionForHR(hr);
+ if (count == 0)
+ return new TItem[0];
+
+ var result = new TItem[count];
+ hr = getter(entity, count, out count, result);
+ ThrowExceptionForHR(hr);
+ ValidateItems(count, result.Length);
+ return result;
+ }
+
+ public static ISymUnmanagedDocument[] GetDocuments(this ISymUnmanagedReader reader)
+ {
+ return GetItems(reader, (ISymUnmanagedReader a, int b, out int c, ISymUnmanagedDocument[] d) => a.GetDocuments(b, out c, d));
+ }
+
+ internal static string GetName(this ISymUnmanagedDocument document)
+ {
+ return ToString(GetItems(document, (ISymUnmanagedDocument a, int b, out int c, char[] d) => a.GetUrl(b, out c, d)));
+ }
+
+ internal static void ThrowExceptionForHR(int hr)
+ {
+ // E_FAIL indicates "no info".
+ // E_NOTIMPL indicates a lack of ISymUnmanagedReader support (in a particular implementation).
+ if (hr < 0 && hr != E_FAIL && hr != E_NOTIMPL)
+ {
+ Marshal.ThrowExceptionForHR(hr, new IntPtr(-1));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CommandLine/Project.json b/src/CommandLine/Project.json
new file mode 100644
index 000000000..e5209253a
--- /dev/null
+++ b/src/CommandLine/Project.json
@@ -0,0 +1,17 @@
+{
+ "dependencies": {
+ "Microsoft.DiaSymReader.Native": "1.2.0-rc",
+ "Microsoft.DiaSymReader": "1.0.6"
+ },
+
+ "frameworks": {
+ "net45": { }
+ },
+
+ "runtimes": {
+ "win7-x86": { },
+ "win7-x64": { },
+ "win8-x86": { },
+ "win8-x64": { }
+ }
+}
\ No newline at end of file