{"action":"created","comment":{"url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/comments/92911299","pull_request_review_id":13437151,"id":92911299,"diff_hunk":"@@ -22,428 +22,693 @@ namespace NuGet.Commands\n {\n     public static class BuildAssetsUtils\n     {\n+        private static readonly XNamespace Namespace = XNamespace.Get(\"http://schemas.microsoft.com/developer/msbuild/2003\");\n         internal static readonly string CrossTargetingCondition = \"'$(TargetFramework)' == ''\";\n         internal static readonly string TargetFrameworkCondition = \"'$(TargetFramework)' == '{0}'\";\n+        internal static readonly string LanguageCondition = \"'$(Language)' == '{0}'\";\n+        internal static readonly string NegativeLanguageCondition = \"'$(Language)' != '{0}'\";\n         internal static readonly string ExcludeAllCondition = \"'$(ExcludeRestorePackageImports)' != 'true'\";\n+        private const string TargetsExtension = \".targets\";\n+        private const string PropsExtension = \".props\";\n \n-        internal static MSBuildRestoreResult RestoreMSBuildFiles(PackageSpec project,\n-            IEnumerable<RestoreTargetGraph> targetGraphs,\n-            IReadOnlyList<NuGetv3LocalRepository> repositories,\n-            RemoteWalkContext context,\n-            RestoreRequest request,\n-            Dictionary<RestoreTargetGraph, Dictionary<string, LibraryIncludeFlags>> includeFlagGraphs)\n+        /// <summary>\n+        /// The macros that we may use in MSBuild to replace path roots.\n+        /// </summary>\n+        public static readonly string[] MacroCandidates = new[]\n         {\n-            var targetsPath = Path.Combine(request.RestoreOutputPath, $\"{project.Name}.nuget.targets\");\n-            var propsPath = Path.Combine(request.RestoreOutputPath, $\"{project.Name}.nuget.props\");\n+            \"UserProfile\", // e.g. C:\\users\\myusername\n+        };\n \n-            if (request.RestoreOutputType == RestoreOutputType.NETCore)\n+        /// <summary>\n+        /// Write XML to disk.\n+        /// Delete files which do not have new XML.\n+        /// </summary>\n+        public static void WriteFiles(IEnumerable<MSBuildOutputFile> files, ILogger log)\n+        {\n+            foreach (var file in files)\n             {\n-                var projFileName = Path.GetFileName(request.Project.RestoreMetadata.ProjectPath);\n+                if (file.Content == null)\n+                {\n+                    // Remove the file if the XML is null\n+                    FileUtility.Delete(file.Path);\n+                }\n+                else\n+                {\n+                    log.LogMinimal(string.Format(CultureInfo.CurrentCulture, Strings.Log_GeneratingMsBuildFile, file.Path));\n \n-                targetsPath = Path.Combine(request.RestoreOutputPath, $\"{projFileName}.nuget.g.targets\");\n-                propsPath = Path.Combine(request.RestoreOutputPath, $\"{projFileName}.nuget.g.props\");\n+                    // Create the directory if it doesn't exist\n+                    Directory.CreateDirectory(Path.GetDirectoryName(file.Path));\n+\n+                    // Write out XML file\n+                    WriteXML(file.Path, file.Content);\n+                }\n             }\n+        }\n+\n+        /// <summary>\n+        /// Create MSBuild targets and props files.\n+        /// Null will be returned for files that should be removed.\n+        /// </summary>\n+        public static List<MSBuildOutputFile> GenerateMultiTargetFailureFiles(\n+            string targetsPath,\n+            string propsPath,\n+            RestoreOutputType restoreType)\n+        {\n+            XDocument targetsXML = null;\n+            XDocument propsXML = null;\n \n-            // Non-Msbuild projects should skip targets and treat it as success\n-            if (!context.IsMsBuildBased && !ForceWriteTargets())\n+            // Create an error file for MSBuild to stop the build.\n+            targetsXML = GenerateMultiTargetFrameworkWarning();\n+\n+            if (restoreType == RestoreOutputType.NETCore)\n             {\n-                return new MSBuildRestoreResult(targetsPath, propsPath, success: true);\n+                propsXML = GenerateEmptyImportsFile();\n             }\n \n-            // Invalid msbuild projects should write out an msbuild error target\n-            if (!targetGraphs.Any())\n+            var files = new List<MSBuildOutputFile>()\n             {\n-                return new MSBuildRestoreResult(targetsPath, propsPath, success: false);\n-            }\n+                new MSBuildOutputFile(propsPath, propsXML),\n+                new MSBuildOutputFile(targetsPath, targetsXML),\n+            };\n \n-            // Framework -> (targets, props)\n-            var buildAssetsByFramework = new Dictionary<NuGetFramework, TargetsAndProps>();\n+            return files;\n+        }\n \n-            // Get assets for each framework\n-            foreach (var projectFramework in project.TargetFrameworks.Select(f => f.FrameworkName))\n+        private static string ReplacePathsWithMacros(string path)\n+        {\n+            foreach (var macroName in MacroCandidates)\n             {\n-                var targetsAndProps =\n-                    GetTargetsAndPropsForFramework(\n-                        targetGraphs,\n-                        repositories,\n-                        context,\n-                        request,\n-                        includeFlagGraphs,\n-                        projectFramework);\n+                string macroValue = Environment.GetEnvironmentVariable(macroName);\n+                if (!string.IsNullOrEmpty(macroValue)\n+                    && path.StartsWith(macroValue, StringComparison.OrdinalIgnoreCase))\n+                {\n+                    path = $\"$({macroName})\" + $\"{path.Substring(macroValue.Length)}\";\n+                }\n \n-                buildAssetsByFramework.Add(projectFramework, targetsAndProps);\n+                break;\n             }\n \n-            var props = new List<MSBuildRestoreImportGroup>();\n-            var targets = new List<MSBuildRestoreImportGroup>();\n+            return path;\n+        }\n+\n+        public static XDocument GenerateMultiTargetFrameworkWarning()\n+        {\n+            var doc = GenerateEmptyImportsFile();\n+\n+            doc.Root.Add(new XElement(Namespace + \"Target\",\n+                        new XAttribute(\"Name\", \"EmitMSBuildWarning\"),\n+                        new XAttribute(\"BeforeTargets\", \"Build\"),\n+\n+                        new XElement(Namespace + \"Warning\",\n+                            new XAttribute(\"Text\", Strings.MSBuildWarning_MultiTarget))));\n+\n+            return doc;\n+        }\n+\n+        /// <summary>\n+        /// Add standard properties to only props file if it exists, otherwise the targets.\n+        /// </summary>\n+        public static void AddNuGetPropertiesToFirstImport(IEnumerable<MSBuildOutputFile> files,\n+            IEnumerable<string> packageFolders,\n+            string repositoryRoot,\n+            RestoreOutputType outputType,\n+            bool success)\n+        {\n+            var firstImport = files.Where(file => file.Content != null)\n+                .OrderByDescending(file => file.Path.EndsWith(PropsExtension) ? 1 : 0)\n+                .FirstOrDefault();\n \n-            // Conditionals for targets and props are only supported by NETCore\n-            if (project.RestoreMetadata?.OutputType == RestoreOutputType.NETCore)\n+            if (firstImport != null)\n             {\n-                // Add additional conditionals for cross targeting\n-                var isCrossTargeting = request.Project.RestoreMetadata.CrossTargeting\n-                    || request.Project.TargetFrameworks.Count > 1;\n+                AddNuGetProperties(firstImport.Content, packageFolders, repositoryRoot, outputType, success);\n+            }\n+        }\n \n-                Debug.Assert((!request.Project.RestoreMetadata.CrossTargeting && (request.Project.TargetFrameworks.Count < 2)\n-                    || (request.Project.RestoreMetadata.CrossTargeting)),\n-                    \"Invalid crosstargeting and framework count combination\");\n+        /// <summary>\n+        /// Apply standard properties in a property group.\n+        /// </summary>\n+        public static void AddNuGetProperties(XDocument doc, IEnumerable<string> packageFolders, string repositoryRoot, RestoreOutputType outputType, bool success)\n+        {\n+            var projectStyle = \"Unknown\";\n \n-                if (isCrossTargeting)\n-                {\n-                    // Find all global targets from buildCrossTargeting\n-                    var crossTargetingAssets = GetTargetsAndPropsForCrossTargeting(\n-                            targetGraphs,\n-                            repositories,\n-                            context,\n-                            request,\n-                            includeFlagGraphs);\n-\n-                    var crossProps = new MSBuildRestoreImportGroup();\n-                    crossProps.Position = 0;\n-                    crossProps.Conditions.Add(CrossTargetingCondition);\n-                    crossProps.Imports.AddRange(crossTargetingAssets.Props);\n-                    props.Add(crossProps);\n-\n-                    var crossTargets = new MSBuildRestoreImportGroup();\n-                    crossTargets.Position = 0;\n-                    crossTargets.Conditions.Add(CrossTargetingCondition);\n-                    crossTargets.Imports.AddRange(crossTargetingAssets.Targets);\n-                    targets.Add(crossTargets);\n-                }\n+            if (outputType == RestoreOutputType.NETCore)\n+            {\n+                projectStyle = \"PackageReference\";\n+            }\n+            else if (outputType == RestoreOutputType.UAP)\n+            {\n+                projectStyle = \"ProjectJson\";\n+            }\n \n-                // Find TFM specific assets from the build folder\n-                foreach (var pair in buildAssetsByFramework)\n-                {\n-                    // There could be multiple string matches\n-                    foreach (var match in GetMatchingFrameworkStrings(project, pair.Key))\n-                    {\n-                        var frameworkCondition = string.Format(CultureInfo.InvariantCulture, TargetFrameworkCondition, match);\n+            doc.Root.AddFirst(\n+                new XElement(Namespace + \"PropertyGroup\",\n+                            new XAttribute(\"Condition\", $\" {ExcludeAllCondition} \"),\n+                            GetProperty(\"RestoreSuccess\", success.ToString()),\n+                            GetProperty(\"RestoreTool\", \"NuGet\"),\n+                            GetProperty(\"NuGetPackageRoot\", ReplacePathsWithMacros(repositoryRoot)),\n+                            GetProperty(\"NuGetPackageFolders\", string.Join(\";\", packageFolders)),\n+                            GetProperty(\"NuGetProjectStyle\", projectStyle),\n+                            GetProperty(\"NuGetToolVersion\", MinClientVersionUtility.GetNuGetClientVersion().ToFullString())));\n+        }\n \n-                        // Add entries regardless of if imports exist,\n-                        // this is needed to trigger conditionals\n-                        var propsGroup = new MSBuildRestoreImportGroup();\n+        /// <summary>\n+        /// Get empty file with the base properties.\n+        /// </summary>\n+        public static XDocument GenerateEmptyImportsFile()\n+        {\n+            var doc = new XDocument(\n+                new XDeclaration(\"1.0\", \"utf-8\", \"no\"),\n \n-                        if (isCrossTargeting)\n-                        {\n-                            propsGroup.Conditions.Add(frameworkCondition);\n-                        }\n+                new XElement(Namespace + \"Project\",\n+                    new XAttribute(\"ToolsVersion\", \"14.0\")));\n \n-                        propsGroup.Imports.AddRange(pair.Value.Props);\n-                        propsGroup.Position = 1;\n-                        props.Add(propsGroup);\n+            return doc;\n+        }\n \n-                        var targetsGroup = new MSBuildRestoreImportGroup();\n+        public static XElement GetProperty(string propertyName, string content)\n+        {\n+            return new XElement(Namespace + propertyName,\n+                            new XAttribute(\"Condition\", $\" '$({propertyName})' == '' \"),\n+                            content);\n+        }\n \n-                        if (isCrossTargeting)\n-                        {\n-                            targetsGroup.Conditions.Add(frameworkCondition);\n-                        }\n+        public static XElement GenerateImport(string path)\n+        {\n+            return new XElement(Namespace + \"Import\",\n+                                new XAttribute(\"Project\", path),\n+                                new XAttribute(\"Condition\", $\"Exists('{path}')\"));\n+        }\n+\n+        public static XElement GenerateContentFilesItem(string path, LockFileContentFile item, string packageId, string packageVersion)\n+        {\n+            var entry = new XElement(Namespace + item.BuildAction.Value,\n+                                new XAttribute(\"Include\", path),\n+                                new XAttribute(\"Condition\", $\"Exists('{path}')\"),\n+                                new XElement(Namespace + \"NuGetPackageId\", packageId),\n+                                new XElement(Namespace + \"NuGetPackageVersion\", packageVersion),\n+                                new XElement(Namespace + \"NuGetItemType\", item.BuildAction));\n+\n+            var privateFlag = false;\n+\n+            if (item.CopyToOutput)\n+            {\n+                var outputPath = item.OutputPath ?? item.PPOutputPath;\n+\n+                if (outputPath != null)\n+                {\n+                    // Convert / to \\\n+                    outputPath = LockFileUtils.ToDirectorySeparator(outputPath);\n \n-                        targetsGroup.Imports.AddRange(pair.Value.Targets);\n-                        targetsGroup.Position = 1;\n-                        targets.Add(targetsGroup);\n+                    privateFlag = true;\n+                    entry.Add(new XElement(Namespace + \"CopyToOutputDirectory\", \"PreserveNewest\"));\n+\n+                    entry.Add(new XElement(Namespace + \"TargetPath\", outputPath));\n+\n+                    var destinationSubDirectory = Path.GetDirectoryName(outputPath);\n+\n+                    if (!string.IsNullOrEmpty(destinationSubDirectory))\n+                    {\n+                        entry.Add(new XElement(Namespace + \"DestinationSubDirectory\", destinationSubDirectory + Path.DirectorySeparatorChar));\n                     }\n                 }\n             }\n-            else\n-            {\n-                // Copy targets and props over, there can only be 1 tfm here\n-                // No conditionals are added\n-                var targetsAndProps = buildAssetsByFramework.First();\n \n-                var propsGroup = new MSBuildRestoreImportGroup();\n-                propsGroup.Imports.AddRange(targetsAndProps.Value.Props);\n-                props.Add(propsGroup);\n+            entry.Add(new XElement(Namespace + \"Private\", privateFlag.ToString()));\n \n-                var targetsGroup = new MSBuildRestoreImportGroup();\n-                targetsGroup.Imports.AddRange(targetsAndProps.Value.Targets);\n-                targets.Add(targetsGroup);\n-            }\n+            // Remove contentFile/lang/tfm/ from start of the path\n+            var linkPath = string.Join(string.Empty + Path.DirectorySeparatorChar, \n+                    item.Path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)\n+                        .Skip(3)\n+                        .ToArray());\n \n-            // Add exclude all condition to all groups\n-            foreach (var group in props.Concat(targets))\n+            if (linkPath != null)\n             {\n-                group.Conditions.Add(ExcludeAllCondition);\n+                entry.Add(new XElement(Namespace + \"Link\", linkPath));\n             }\n \n-            // Targets files contain a macro for the repository root. If only the user package folder was used\n-            // allow a replacement. If fallback folders were used the macro cannot be applied.\n-            // Do not use macros for fallback folders. Use only the first repository which is the user folder.\n-            var repositoryRoot = repositories.First().RepositoryRoot;\n-\n-            // Create a result which may be committed to disk later.\n-            return new MSBuildRestoreResult(\n-                targetsPath,\n-                propsPath,\n-                repositoryRoot,\n-                props,\n-                targets);\n+            return entry;\n         }\n \n-        private static HashSet<string> GetMatchingFrameworkStrings(PackageSpec spec, NuGetFramework framework)\n+        /// <summary>\n+        /// Returns null if the result should not exist on disk.\n+        /// </summary>\n+        public static XDocument GenerateMSBuildFile(List<MSBuildRestoreItemGroup> groups, RestoreOutputType outputType)\n         {\n-            // Ignore case since msbuild does\n-            var matches = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n+            XDocument doc = null;\n \n-            matches.UnionWith(spec.RestoreMetadata.OriginalTargetFrameworks\n-                .Where(s => framework.Equals(NuGetFramework.Parse(s))));\n-\n-            // If there were no matches, use the generated name\n-            if (matches.Count < 1)\n+            // Always write out netcore props/targets. For project.json only write the file if it has items.\n+            if (outputType == RestoreOutputType.NETCore || groups.SelectMany(e => e.Items).Any())\n             {\n-                matches.Add(framework.GetShortFolderName());\n+                doc = GenerateEmptyImportsFile();\n+\n+                // Add import groups, order by position, then by the conditions to keep the results deterministic\n+                // Skip empty groups\n+                foreach (var group in groups\n+                    .Where(e => e.Items.Count > 0)\n+                    .OrderBy(e => e.Position)\n+                    .ThenBy(e => e.Condition, StringComparer.OrdinalIgnoreCase))\n+                {\n+                    var itemGroup = new XElement(Namespace + group.RootName, group.Items);\n+\n+                    // Add a conditional statement if multiple TFMs exist or cross targeting is present\n+                    var conditionValue = group.Condition;\n+                    if (!string.IsNullOrEmpty(conditionValue))\n+                    {\n+                        itemGroup.Add(new XAttribute(\"Condition\", conditionValue));\n+                    }\n+\n+                    // Add itemgroup to file\n+                    doc.Root.Add(itemGroup);\n+                }\n             }\n \n-            return matches;\n+            return doc;\n         }\n \n-        private static TargetsAndProps GetTargetsAndPropsForFramework(\n-            IEnumerable<RestoreTargetGraph> targetGraphs,\n-            IReadOnlyList<NuGetv3LocalRepository> repositories,\n-            RemoteWalkContext context,\n-            RestoreRequest request,\n-            Dictionary<RestoreTargetGraph,\n-            Dictionary<string, LibraryIncludeFlags>> includeFlagGraphs,\n-            NuGetFramework projectFramework)\n+        public static void WriteXML(string path, XDocument doc)\n         {\n-            var result = new TargetsAndProps();\n-\n-            // Skip runtime graphs, msbuild targets may not come from RID specific packages\n-            var graph = targetGraphs\n-                .Single(g => string.IsNullOrEmpty(g.RuntimeIdentifier) && g.Framework.Equals(projectFramework));\n-\n-            // Gather props and targets to write out\n-            var buildGroupSets = GetMSBuildAssets(\n-                context,\n-                graph,\n-                request.Project,\n-                includeFlagGraphs,\n-                graph.Conventions.Patterns.MSBuildFiles);\n-\n-            // Second find the nearest group for each framework\n-            foreach (var buildGroupSetsEntry in buildGroupSets)\n-            {\n-                var libraryIdentity = buildGroupSetsEntry.Key;\n-                var buildGroupSet = buildGroupSetsEntry.Value;\n-\n-                // Find the nearest msbuild group, this can include the root level Any group.\n-                var buildItems = NuGetFrameworkUtility.GetNearest(\n-                        buildGroupSet,\n-                        graph.Framework,\n-                        group =>\n-                            group.Properties[ManagedCodeConventions.PropertyNames.TargetFrameworkMoniker]\n-                                as NuGetFramework);\n-\n-                // Check if compatible build assets exist\n-                if (buildItems != null)\n+            FileUtility.Replace((outputPath) =>\n+            {\n+                using (var output = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))\n                 {\n-                    AddPropsAndTargets(repositories, libraryIdentity, buildItems, result);\n+                    doc.Save(output);\n                 }\n+            },\n+            path);\n+        }\n+\n+        public static string GetImportPath(string importPath, string repositoryRoot)\n+        {\n+            var path = importPath;\n+\n+            if (importPath.StartsWith(repositoryRoot, StringComparison.Ordinal))\n+            {\n+                path = $\"$(NuGetPackageRoot){importPath.Substring(repositoryRoot.Length)}\";\n+            }\n+            else\n+            {\n+                path = ReplacePathsWithMacros(importPath);\n             }\n \n-            return result;\n+            return path;\n         }\n \n-        private static TargetsAndProps GetTargetsAndPropsForCrossTargeting(\n-            IEnumerable<RestoreTargetGraph> targetGraphs,\n-            IReadOnlyList<NuGetv3LocalRepository> repositories,\n-            RemoteWalkContext context,\n-            RestoreRequest request,\n-            Dictionary<RestoreTargetGraph,\n-            Dictionary<string, LibraryIncludeFlags>> includeFlagGraphs)\n+        /// <summary>\n+        /// Check if the file has changes compared to the original on disk.\n+        /// </summary>\n+        public static bool HasChanges(XDocument newFile, string path, ILogger log)\n         {\n-            var result = new TargetsAndProps();\n-\n-            // Skip runtime graphs, msbuild targets may not come from RID specific packages\n-            // Order the graphs by framework to make this deterministic for scenarios where\n-            // TFMs disagree on the dependency order, there is little that can be done for\n-            // conflicts where A->B for TFM1 and B->A for TFM2.\n-            var ridlessGraphs = targetGraphs\n-                .Where(g => string.IsNullOrEmpty(g.RuntimeIdentifier))\n-                .OrderBy(g => g.Framework, new NuGetFrameworkSorter());\n-\n-            // Gather props and targets to write out\n-            foreach (var graph in ridlessGraphs)\n-            {\n-                var globalGroupSets = GetMSBuildAssets(\n-                    context,\n-                    graph,\n-                    request.Project,\n-                    includeFlagGraphs,\n-                    graph.Conventions.Patterns.MSBuildCrossTargetingFiles);\n-\n-                // Check if compatible build assets exist\n-                foreach (var globalGroupEntry in globalGroupSets)\n+            if (newFile == null)\n+            {\n+                // The file should be deleted if it is null.\n+                return File.Exists(path);\n+            }\n+            else\n+            {\n+                var existing = ReadExisting(path, log);\n+\n+                if (existing != null)\n                 {\n-                    var libraryIdentity = globalGroupEntry.Key;\n-                    var buildGroupSet = globalGroupEntry.Value;\n+                    // Use a simple string compare to check if the files match\n+                    // This can be optimized in the future, but generally these are very small files.\n+                    return !newFile.ToString().Equals(existing.ToString(), StringComparison.Ordinal);\n+                }\n+            }\n+\n+            return true;\n+        }\n \n-                    Debug.Assert(buildGroupSet.Length < 2, \"Unexpected number of build global asset groups\");\n+        public static XDocument ReadExisting(string path, ILogger log)\n+        {\n+            XDocument result = null;\n \n-                    // There can only be one group since there are no TFMs here.\n-                    if (buildGroupSet.Length == 1)\n+            if (File.Exists(path))\n+            {\n+                try\n+                {\n+                    using (var output = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None))\n                     {\n-                        // Add all targets and props from buildCrossTargeting\n-                        // Note: AddPropsAndTargets handles de-duping file paths. Since these non-TFM specific\n-                        // files are found for every TFM it is likely that there will be duplicates going in.\n-                        AddPropsAndTargets(\n-                                repositories,\n-                                libraryIdentity,\n-                                buildGroupSet[0],\n-                                result);\n+                        result = XDocument.Load(output);\n                     }\n                 }\n+                catch (Exception ex)\n+                {\n+                    // Log a debug message and ignore, this will force an overwrite\n+                    log.LogDebug($\"Failed to open imports file: {path} Error: {ex.Message}\");\n+                }\n             }\n \n             return result;\n         }\n \n-        /// <summary>\n-        /// Check if NUGET_XPROJ_WRITE_TARGETS is true.\n-        /// </summary>\n-        private static bool ForceWriteTargets()\n+        public static List<MSBuildOutputFile> GetMSBuildOutputFiles(PackageSpec project,\n+            LockFile assetsFile,\n+            IEnumerable<RestoreTargetGraph> targetGraphs,\n+            IReadOnlyList<NuGetv3LocalRepository> repositories,\n+            RestoreRequest request,\n+            bool restoreSuccess,\n+            ILogger log)\n         {\n-            var envVar = Environment.GetEnvironmentVariable(\"NUGET_XPROJ_WRITE_TARGETS\");\n+            // Generate file names\n+            var targetsPath = string.Empty;\n+            var propsPath = string.Empty;\n \n-            bool forceWriteTargets = false;\n-            if (!string.IsNullOrEmpty(envVar))\n+            if (request.RestoreOutputType == RestoreOutputType.NETCore)\n             {\n-                Boolean.TryParse(envVar, out forceWriteTargets);\n+                // PackageReference style projects\n+                var projFileName = Path.GetFileName(request.Project.RestoreMetadata.ProjectPath);\n+\n+                targetsPath = Path.Combine(request.RestoreOutputPath, $\"{projFileName}.nuget.g.targets\");\n+                propsPath = Path.Combine(request.RestoreOutputPath, $\"{projFileName}.nuget.g.props\");\n             }\n+            else\n+            {\n+                // Project.json style projects\n+                var dir = Path.GetDirectoryName(project.FilePath);\n \n-            return forceWriteTargets;\n-        }\n+                targetsPath = Path.Combine(dir, $\"{project.Name}.nuget.targets\");\n+                propsPath = Path.Combine(dir, $\"{project.Name}.nuget.props\");\n+            }\n \n-        /// <summary>\n-        /// Find all included msbuild assets for a graph.\n-        /// </summary>\n-        private static Dictionary<PackageIdentity, ContentItemGroup[]> GetMSBuildAssets(\n-            RemoteWalkContext context,\n-            RestoreTargetGraph graph,\n-            PackageSpec project,\n-            Dictionary<RestoreTargetGraph, Dictionary<string, LibraryIncludeFlags>> includeFlagGraphs,\n-            PatternSet patternSet)\n-        {\n-            var buildGroupSets = new Dictionary<PackageIdentity, ContentItemGroup[]>();\n+            // Targets files contain a macro for the repository root. If only the user package folder was used\n+            // allow a replacement. If fallback folders were used the macro cannot be applied.\n+            // Do not use macros for fallback folders. Use only the first repository which is the user folder.\n+            var repositoryRoot = repositories.First().RepositoryRoot;\n \n-            var flattenedFlags = IncludeFlagUtils.FlattenDependencyTypes(includeFlagGraphs, project, graph);\n+            // Invalid msbuild projects should write out an msbuild error target\n+            if (!targetGraphs.Any())\n+            {\n+                return GenerateMultiTargetFailureFiles(\n+                    targetsPath,\n+                    propsPath,\n+                    request.RestoreOutputType);\n+            }\n \n-            // convert graph items to package dependency info list\n-            var dependencies = ConvertToPackageDependencyInfo(graph.Flattened);\n+            // Add additional conditionals for multi targeting\n+            var multiTargetingFromMetadata = (request.Project.RestoreMetadata?.CrossTargeting == true);\n \n-            // sort graph nodes by dependencies order\n-            var sortedItems = TopologicalSortUtility.SortPackagesByDependencyOrder(dependencies);\n+            var isMultiTargeting = multiTargetingFromMetadata\n+                || request.Project.TargetFrameworks.Count > 1;\n \n-            // First find all msbuild items in the packages\n-            foreach (var library in sortedItems)\n-            {\n-                var includeLibrary = true;\n+            // ItemGroups for each file.\n+            var props = new List<MSBuildRestoreItemGroup>();\n+            var targets = new List<MSBuildRestoreItemGroup>();\n+\n+            // Skip runtime graphs, msbuild targets may not come from RID specific packages.\n+            var ridlessTargets = assetsFile.Targets\n+                .Where(e => string.IsNullOrEmpty(e.RuntimeIdentifier));\n \n-                LibraryIncludeFlags libraryFlags;\n-                if (flattenedFlags.TryGetValue(library.Id, out libraryFlags))\n+            foreach (var ridlessTarget in ridlessTargets)\n+            {\n+                // There could be multiple string matches from the MSBuild project.\n+                var frameworkConditions = GetMatchingFrameworkStrings(project, ridlessTarget.TargetFramework)\n+                    .Select(match => string.Format(CultureInfo.InvariantCulture, TargetFrameworkCondition, match))\n+                    .ToArray();\n+\n+                // Find matching target in the original target graphs.\n+                var targetGraph = targetGraphs.FirstOrDefault(e =>\n+                    string.IsNullOrEmpty(e.RuntimeIdentifier)\n+                    && ridlessTarget.TargetFramework == e.Framework);\n+\n+                // Sort by dependency order, child package assets should appear higher in the\n+                // msbuild targets and props files so that parents can depend on them.\n+                var sortedGraph = TopologicalSortUtility.SortPackagesByDependencyOrder(ConvertToPackageDependencyInfo(targetGraph.Flattened));\n+\n+                // Filter out to packages only, exclude projects.\n+                var packageType = new HashSet<string>(\n+                    targetGraph.Flattened.Where(e => e.Key.Type == LibraryType.Package)\n+                        .Select(e => e.Key.Name),\n+                    StringComparer.OrdinalIgnoreCase);\n+\n+                // Package -> PackageInfo\n+                // PackageInfo is kept lazy to avoid hitting the disk for packages\n+                // with no relevant assets.\n+                var sortedPackages = sortedGraph.Where(e => packageType.Contains(e.Id))\n+                                                .Select(sortedPkg =>\n+                                                    new KeyValuePair<LockFileTargetLibrary, Lazy<LocalPackageSourceInfo>>(\n+                                                        key: ridlessTarget.Libraries.FirstOrDefault(assetsPkg =>\n+                                                            sortedPkg.Version == assetsPkg.Version\n+                                                            && sortedPkg.Id.Equals(assetsPkg.Name, StringComparison.OrdinalIgnoreCase)),\n+                                                        value: new Lazy<LocalPackageSourceInfo>(() =>\n+                                                            NuGetv3LocalRepositoryUtility.GetPackage(\n+                                                                repositories,\n+                                                                sortedPkg.Id,\n+                                                                sortedPkg.Version))))\n+                                                .Where(e => e.Key != null)\n+                                                .ToArray();\n+\n+                // build/ {packageId}.targets\n+                var buildTargetsGroup = new MSBuildRestoreItemGroup();\n+                buildTargetsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;\n+                buildTargetsGroup.Position = 2;\n+\n+                buildTargetsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>\n+                    pkg.Key.Build.WithExtension(TargetsExtension)\n+                        .Where(e => pkg.Value.Exists())\n+                        .Select(e => pkg.Value.GetAbsolutePath(e)))\n+                        .Select(path => GetImportPath(path, repositoryRoot))\n+                        .Select(GenerateImport));\n+\n+                targets.AddRange(GetGroupsWithConditions(buildTargetsGroup, isMultiTargeting, frameworkConditions));\n+\n+                // props/ {packageId}.props\n+                var buildPropsGroup = new MSBuildRestoreItemGroup();\n+                buildPropsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;\n+                buildPropsGroup.Position = 2;\n+\n+                buildPropsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>\n+                    pkg.Key.Build.WithExtension(PropsExtension)\n+                        .Where(e => pkg.Value.Exists())\n+                        .Select(e => pkg.Value.GetAbsolutePath(e)))\n+                        .Select(path => GetImportPath(path, repositoryRoot))\n+                        .Select(GenerateImport));\n+\n+                props.AddRange(GetGroupsWithConditions(buildPropsGroup, isMultiTargeting, frameworkConditions));\n+\n+                if (isMultiTargeting)\n                 {\n-                    includeLibrary = libraryFlags.HasFlag(LibraryIncludeFlags.Build);\n+                    // buildMultiTargeting/ {packageId}.targets\n+                    var buildCrossTargetsGroup = new MSBuildRestoreItemGroup();\n+                    buildCrossTargetsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;\n+                    buildCrossTargetsGroup.Position = 0;\n+\n+                    buildCrossTargetsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>\n+                        pkg.Key.BuildMultiTargeting.WithExtension(TargetsExtension)\n+                            .Where(e => pkg.Value.Exists())\n+                            .Select(e => pkg.Value.GetAbsolutePath(e)))\n+                            .Select(path => GetImportPath(path, repositoryRoot))\n+                            .Select(GenerateImport));\n+\n+                    targets.AddRange(GetGroupsWithConditions(buildCrossTargetsGroup, isMultiTargeting, CrossTargetingCondition));\n+\n+                    // buildMultiTargeting/ {packageId}.props\n+                    var buildCrossPropsGroup = new MSBuildRestoreItemGroup();\n+                    buildCrossPropsGroup.RootName = MSBuildRestoreItemGroup.ImportGroup;\n+                    buildCrossPropsGroup.Position = 0;\n+\n+                    buildCrossPropsGroup.Items.AddRange(sortedPackages.SelectMany(pkg =>\n+                        pkg.Key.BuildMultiTargeting.WithExtension(PropsExtension)\n+                            .Where(e => pkg.Value.Exists())\n+                            .Select(e => pkg.Value.GetAbsolutePath(e)))\n+                            .Select(path => GetImportPath(path, repositoryRoot))\n+                            .Select(GenerateImport));\n+\n+                    props.AddRange(GetGroupsWithConditions(buildCrossPropsGroup, isMultiTargeting, CrossTargetingCondition));\n                 }\n \n-                // Skip libraries that do not include build files such as transitive packages\n-                if (includeLibrary)\n+                // ContentFiles are read by the build task, not by NuGet\n+                // for UAP with project.json.\n+                if (request.RestoreOutputType != RestoreOutputType.UAP)\n                 {\n-                    var packageIdentity = new PackageIdentity(library.Id, library.Version);\n-                    IList<string> packageFiles;\n-                    context.PackageFileCache.TryGetValue(packageIdentity, out packageFiles);\n+                    // Create a group for every package, with the nearest from each of allLanguages\n+                    props.AddRange(sortedPackages.Select(pkg =>\n+                         pkg.Key.ContentFiles\n+                                .Where(e => pkg.Value.Exists())\n+                                .OrderBy(e => e.Path, StringComparer.Ordinal)\n+                                .Select(e =>\n+                                    new Tuple<LockFileTargetLibrary, LockFileContentFile, string>(\n+                                        item1: pkg.Key,\n+                                        item2: e,\n+                                        item3: pkg.Value.GetAbsolutePath(GetImportPath(e.Path, repositoryRoot)))))\n+                         .SelectMany(e => GetLanguageGroups(e))\n+                         .SelectMany(group => GetGroupsWithConditions(group, isMultiTargeting, frameworkConditions)));\n+                }\n+            }\n \n-                    if (packageFiles != null)\n-                    {\n-                        var contentItemCollection = new ContentItemCollection();\n-                        contentItemCollection.Load(packageFiles);\n+            // Add exclude all condition to all groups\n+            foreach (var group in props.Concat(targets))\n+            {\n+                group.Conditions.Add(ExcludeAllCondition);\n+            }\n \n-                        // Find MSBuild groups\n-                        var buildGroupSet = contentItemCollection\n-                            .FindItemGroups(patternSet)\n-                            .ToArray();\n+            // Create XML, these may be null if the file should be deleted/not written out.\n+            var propsXML = GenerateMSBuildFile(props, request.RestoreOutputType);\n+            var targetsXML = GenerateMSBuildFile(targets, request.RestoreOutputType);\n \n-                        buildGroupSets.Add(packageIdentity, buildGroupSet);\n-                    }\n+            // Return all files to write out or delete.\n+            var files = new List<MSBuildOutputFile>\n+            {\n+                new MSBuildOutputFile(propsPath, propsXML),\n+                new MSBuildOutputFile(targetsPath, targetsXML)\n+            };\n+\n+            var packageFolders = repositories.Select(e => e.RepositoryRoot);\n+\n+            AddNuGetPropertiesToFirstImport(files, packageFolders, repositoryRoot, request.RestoreOutputType, restoreSuccess);\n+\n+            return files;\n+        }\n+\n+        private static IEnumerable<string> GetLanguageConditions(string language, SortedSet<string> allLanguages)\n+        {\n+            if (PackagingConstants.AnyCodeLanguage.Equals(language, StringComparison.OrdinalIgnoreCase))\n+            {\n+                // Must not be any of the other package languages.\n+                foreach (var lang in allLanguages)\n+                {\n+                    yield return string.Format(CultureInfo.InvariantCulture, NegativeLanguageCondition, GetLanguage(lang));\n                 }\n             }\n-\n-            return buildGroupSets;\n+            else\n+            {\n+                // Must be the language.\n+                yield return string.Format(CultureInfo.InvariantCulture, LanguageCondition, GetLanguage(language));\n+            }\n         }\n \n-        private static HashSet<PackageDependencyInfo> ConvertToPackageDependencyInfo(\n-            ISet<GraphItem<RemoteResolveResult>> items)\n+        public static string GetLanguage(string nugetLanguage)\n         {\n-            var result = new HashSet<PackageDependencyInfo>(PackageIdentity.Comparer);\n+            var lang = nugetLanguage.ToUpperInvariant();\n \n-            foreach (var item in items)\n+            // Translate S -> #\n+            switch (lang)\n             {\n-                var dependencies =\n-                    item.Data?.Dependencies?.Select(\n-                        dependency => new PackageDependency(dependency.Name, VersionRange.All));\n+                case \"CS\":\n+                    return \"C#\";\n+                case \"FS\":\n+                    return \"F#\";\n+            }\n \n-                result.Add(new PackageDependencyInfo(item.Key.Name, item.Key.Version, dependencies));\n+            // Return the language as it is\n+            return lang;\n+        }\n+\n+        private static IEnumerable<MSBuildRestoreItemGroup> GetLanguageGroups(\n+            IEnumerable<Tuple<LockFileTargetLibrary, LockFileContentFile, string>> items)\n+        {\n+            var currentItems = items.ToArray();\n+\n+            if (currentItems.Length == 0)\n+            {\n+                // Noop fast if this does not have content files.\n+                return Enumerable.Empty<MSBuildRestoreItemGroup>();\n             }\n \n-            return result;\n+            var packageId = currentItems[0].Item1.Name;\n+            var packageVersion = currentItems[0].Item1.Version.ToNormalizedString();\n+\n+            // Find all languages used for the any group condition\n+            var allLanguages = new SortedSet<string>(\n+                currentItems.Select(e => e.Item2.CodeLanguage)\n+                            .Where(s => !PackagingConstants.AnyCodeLanguage.Equals(s, StringComparison.OrdinalIgnoreCase)),\n+                StringComparer.OrdinalIgnoreCase);\n+\n+            // Convert content file items from a package into an ItemGroup with conditions.\n+            // Remove _._ entries\n+            // Filter empty groups\n+            var groups = currentItems.GroupBy(e => e.Item2.CodeLanguage, StringComparer.OrdinalIgnoreCase)\n+                                .Select(group => MSBuildRestoreItemGroup.Create(\n+                                    rootName: MSBuildRestoreItemGroup.ItemGroup,\n+                                    position: 1,\n+                                    conditions: GetLanguageConditions(group.Key, allLanguages),\n+                                    items: group.Where(e => !e.Item2.Path.EndsWith(PackagingCoreConstants.ForwardSlashEmptyFolder)\n+                                                            && !e.Item2.Path.EndsWith(\".pp\", StringComparison.OrdinalIgnoreCase)) // Skip .pp files\n+                                                .Select(e => GenerateContentFilesItem(e.Item3, e.Item2, packageId, packageVersion))))\n+                                .Where(group => group.Items.Count > 0);\n+\n+            return groups;\n         }\n \n-        /// <summary>\n-        /// Add all valid targets and props to the passed in lists.\n-        /// Modifies targetsAndProps\n-        /// </summary>\n-        private static void AddPropsAndTargets(\n-            IReadOnlyList<NuGetv3LocalRepository> repositories,\n-            PackageIdentity libraryIdentity,\n-            ContentItemGroup buildItems,\n-            TargetsAndProps targetsAndProps)\n+        private static IEnumerable<MSBuildRestoreItemGroup> GetGroupsWithConditions(","path":"src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/BuildAssetsUtils.cs","position":905,"original_position":903,"commit_id":"b2c4540ed77401bc1cd20f56b958826682e9380e","original_commit_id":"60c5d6fe14bd9528e3094800720b92cf78e2b71d","user":{"login":"alpaix","id":8239356,"avatar_url":"https://avatars.githubusercontent.com/u/8239356?v=3","gravatar_id":"","url":"https://api.github.com/users/alpaix","html_url":"https://github.com/alpaix","followers_url":"https://api.github.com/users/alpaix/followers","following_url":"https://api.github.com/users/alpaix/following{/other_user}","gists_url":"https://api.github.com/users/alpaix/gists{/gist_id}","starred_url":"https://api.github.com/users/alpaix/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alpaix/subscriptions","organizations_url":"https://api.github.com/users/alpaix/orgs","repos_url":"https://api.github.com/users/alpaix/repos","events_url":"https://api.github.com/users/alpaix/events{/privacy}","received_events_url":"https://api.github.com/users/alpaix/received_events","type":"User","site_admin":false},"body":">GetGroupsWithConditions [](start = 60, length = 23)\r\n\r\n=> `GenerateGroupsWithConditions`","created_at":"2016-12-17T01:28:59Z","updated_at":"2016-12-17T01:28:59Z","html_url":"https://github.com/NuGet/NuGet.Client/pull/1061#discussion_r92911299","pull_request_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061","_links":{"self":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/comments/92911299"},"html":{"href":"https://github.com/NuGet/NuGet.Client/pull/1061#discussion_r92911299"},"pull_request":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061"}}},"pull_request":{"url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061","id":98254521,"html_url":"https://github.com/NuGet/NuGet.Client/pull/1061","diff_url":"https://github.com/NuGet/NuGet.Client/pull/1061.diff","patch_url":"https://github.com/NuGet/NuGet.Client/pull/1061.patch","issue_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/1061","number":1061,"state":"open","locked":false,"title":"Support for ContentFiles in generated props.","user":{"login":"emgarten","id":6381138,"avatar_url":"https://avatars.githubusercontent.com/u/6381138?v=3","gravatar_id":"","url":"https://api.github.com/users/emgarten","html_url":"https://github.com/emgarten","followers_url":"https://api.github.com/users/emgarten/followers","following_url":"https://api.github.com/users/emgarten/following{/other_user}","gists_url":"https://api.github.com/users/emgarten/gists{/gist_id}","starred_url":"https://api.github.com/users/emgarten/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/emgarten/subscriptions","organizations_url":"https://api.github.com/users/emgarten/orgs","repos_url":"https://api.github.com/users/emgarten/repos","events_url":"https://api.github.com/users/emgarten/events{/privacy}","received_events_url":"https://api.github.com/users/emgarten/received_events","type":"User","site_admin":false},"body":"1. Copied the build task's content files logic into NuGet. Restore will now write out contentFile items to props. .PP files will come later.\r\n1. Added build and buildMultiTargeting to the assets file. This puts all assets in place to simplify restore and removes the need to discover the assets for build separately.  This assets will now count for compatibility.\r\n1. Added buildMultiTargeting, this will replace buildCrossTargeting. Both will exist for now.\r\n1. Combined RestoreResult and MSBuildRestoreResult. MSBuild is now 1st classed in the restore world. Previously it was not since XProj did not use it.\r\n1. Added NuGet properties to the props file, this will help package authors writing targets understand what type of project they are in to bridge gaps between packages.config and PackageReference.\r\n1. Simplified/cleaned up MSBuild targets/props writing for restore.\r\n\r\nFixes https://github.com/NuGet/Home/issues/4098\r\nFixes https://github.com/NuGet/Home/issues/3928\r\nFixes https://github.com/NuGet/Home/issues/3683\r\nFixes https://github.com/NuGet/Home/issues/3399\r\nFixes https://github.com/NuGet/Home/issues/4116\r\n\r\n//cc @alpaix @mishra14 @rohit21agrawal @nkolev92 @jainaashish \r\n\r\n","created_at":"2016-12-15T23:51:36Z","updated_at":"2016-12-17T01:28:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2bb309e6fc948299f1d5525822c3a0909d93e8b1","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061/commits","review_comments_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061/comments","review_comment_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/comments{/number}","comments_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/1061/comments","statuses_url":"https://api.github.com/repos/NuGet/NuGet.Client/statuses/b2c4540ed77401bc1cd20f56b958826682e9380e","head":{"label":"NuGet:dev-emgarten-contentFiles2","ref":"dev-emgarten-contentFiles2","sha":"b2c4540ed77401bc1cd20f56b958826682e9380e","user":{"login":"NuGet","id":968310,"avatar_url":"https://avatars.githubusercontent.com/u/968310?v=3","gravatar_id":"","url":"https://api.github.com/users/NuGet","html_url":"https://github.com/NuGet","followers_url":"https://api.github.com/users/NuGet/followers","following_url":"https://api.github.com/users/NuGet/following{/other_user}","gists_url":"https://api.github.com/users/NuGet/gists{/gist_id}","starred_url":"https://api.github.com/users/NuGet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NuGet/subscriptions","organizations_url":"https://api.github.com/users/NuGet/orgs","repos_url":"https://api.github.com/users/NuGet/repos","events_url":"https://api.github.com/users/NuGet/events{/privacy}","received_events_url":"https://api.github.com/users/NuGet/received_events","type":"Organization","site_admin":false},"repo":{"id":19330736,"name":"NuGet.Client","full_name":"NuGet/NuGet.Client","owner":{"login":"NuGet","id":968310,"avatar_url":"https://avatars.githubusercontent.com/u/968310?v=3","gravatar_id":"","url":"https://api.github.com/users/NuGet","html_url":"https://github.com/NuGet","followers_url":"https://api.github.com/users/NuGet/followers","following_url":"https://api.github.com/users/NuGet/following{/other_user}","gists_url":"https://api.github.com/users/NuGet/gists{/gist_id}","starred_url":"https://api.github.com/users/NuGet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NuGet/subscriptions","organizations_url":"https://api.github.com/users/NuGet/orgs","repos_url":"https://api.github.com/users/NuGet/repos","events_url":"https://api.github.com/users/NuGet/events{/privacy}","received_events_url":"https://api.github.com/users/NuGet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NuGet/NuGet.Client","description":"Core client libraries for NuGet Services","fork":false,"url":"https://api.github.com/repos/NuGet/NuGet.Client","forks_url":"https://api.github.com/repos/NuGet/NuGet.Client/forks","keys_url":"https://api.github.com/repos/NuGet/NuGet.Client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NuGet/NuGet.Client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NuGet/NuGet.Client/teams","hooks_url":"https://api.github.com/repos/NuGet/NuGet.Client/hooks","issue_events_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/events{/number}","events_url":"https://api.github.com/repos/NuGet/NuGet.Client/events","assignees_url":"https://api.github.com/repos/NuGet/NuGet.Client/assignees{/user}","branches_url":"https://api.github.com/repos/NuGet/NuGet.Client/branches{/branch}","tags_url":"https://api.github.com/repos/NuGet/NuGet.Client/tags","blobs_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/refs{/sha}","trees_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NuGet/NuGet.Client/statuses/{sha}","languages_url":"https://api.github.com/repos/NuGet/NuGet.Client/languages","stargazers_url":"https://api.github.com/repos/NuGet/NuGet.Client/stargazers","contributors_url":"https://api.github.com/repos/NuGet/NuGet.Client/contributors","subscribers_url":"https://api.github.com/repos/NuGet/NuGet.Client/subscribers","subscription_url":"https://api.github.com/repos/NuGet/NuGet.Client/subscription","commits_url":"https://api.github.com/repos/NuGet/NuGet.Client/commits{/sha}","git_commits_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/commits{/sha}","comments_url":"https://api.github.com/repos/NuGet/NuGet.Client/comments{/number}","issue_comment_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/comments{/number}","contents_url":"https://api.github.com/repos/NuGet/NuGet.Client/contents/{+path}","compare_url":"https://api.github.com/repos/NuGet/NuGet.Client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NuGet/NuGet.Client/merges","archive_url":"https://api.github.com/repos/NuGet/NuGet.Client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NuGet/NuGet.Client/downloads","issues_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues{/number}","pulls_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls{/number}","milestones_url":"https://api.github.com/repos/NuGet/NuGet.Client/milestones{/number}","notifications_url":"https://api.github.com/repos/NuGet/NuGet.Client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NuGet/NuGet.Client/labels{/name}","releases_url":"https://api.github.com/repos/NuGet/NuGet.Client/releases{/id}","deployments_url":"https://api.github.com/repos/NuGet/NuGet.Client/deployments","created_at":"2014-04-30T23:46:11Z","updated_at":"2016-12-13T12:14:05Z","pushed_at":"2016-12-17T01:15:17Z","git_url":"git://github.com/NuGet/NuGet.Client.git","ssh_url":"git@github.com:NuGet/NuGet.Client.git","clone_url":"https://github.com/NuGet/NuGet.Client.git","svn_url":"https://github.com/NuGet/NuGet.Client","homepage":null,"size":24441,"stargazers_count":127,"watchers_count":127,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":164,"mirror_url":null,"open_issues_count":18,"forks":164,"open_issues":18,"watchers":127,"default_branch":"dev"}},"base":{"label":"NuGet:dev","ref":"dev","sha":"9c7e8b495c9dc2fd02f502737f28363863aa4d38","user":{"login":"NuGet","id":968310,"avatar_url":"https://avatars.githubusercontent.com/u/968310?v=3","gravatar_id":"","url":"https://api.github.com/users/NuGet","html_url":"https://github.com/NuGet","followers_url":"https://api.github.com/users/NuGet/followers","following_url":"https://api.github.com/users/NuGet/following{/other_user}","gists_url":"https://api.github.com/users/NuGet/gists{/gist_id}","starred_url":"https://api.github.com/users/NuGet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NuGet/subscriptions","organizations_url":"https://api.github.com/users/NuGet/orgs","repos_url":"https://api.github.com/users/NuGet/repos","events_url":"https://api.github.com/users/NuGet/events{/privacy}","received_events_url":"https://api.github.com/users/NuGet/received_events","type":"Organization","site_admin":false},"repo":{"id":19330736,"name":"NuGet.Client","full_name":"NuGet/NuGet.Client","owner":{"login":"NuGet","id":968310,"avatar_url":"https://avatars.githubusercontent.com/u/968310?v=3","gravatar_id":"","url":"https://api.github.com/users/NuGet","html_url":"https://github.com/NuGet","followers_url":"https://api.github.com/users/NuGet/followers","following_url":"https://api.github.com/users/NuGet/following{/other_user}","gists_url":"https://api.github.com/users/NuGet/gists{/gist_id}","starred_url":"https://api.github.com/users/NuGet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NuGet/subscriptions","organizations_url":"https://api.github.com/users/NuGet/orgs","repos_url":"https://api.github.com/users/NuGet/repos","events_url":"https://api.github.com/users/NuGet/events{/privacy}","received_events_url":"https://api.github.com/users/NuGet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NuGet/NuGet.Client","description":"Core client libraries for NuGet Services","fork":false,"url":"https://api.github.com/repos/NuGet/NuGet.Client","forks_url":"https://api.github.com/repos/NuGet/NuGet.Client/forks","keys_url":"https://api.github.com/repos/NuGet/NuGet.Client/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NuGet/NuGet.Client/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NuGet/NuGet.Client/teams","hooks_url":"https://api.github.com/repos/NuGet/NuGet.Client/hooks","issue_events_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/events{/number}","events_url":"https://api.github.com/repos/NuGet/NuGet.Client/events","assignees_url":"https://api.github.com/repos/NuGet/NuGet.Client/assignees{/user}","branches_url":"https://api.github.com/repos/NuGet/NuGet.Client/branches{/branch}","tags_url":"https://api.github.com/repos/NuGet/NuGet.Client/tags","blobs_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/refs{/sha}","trees_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NuGet/NuGet.Client/statuses/{sha}","languages_url":"https://api.github.com/repos/NuGet/NuGet.Client/languages","stargazers_url":"https://api.github.com/repos/NuGet/NuGet.Client/stargazers","contributors_url":"https://api.github.com/repos/NuGet/NuGet.Client/contributors","subscribers_url":"https://api.github.com/repos/NuGet/NuGet.Client/subscribers","subscription_url":"https://api.github.com/repos/NuGet/NuGet.Client/subscription","commits_url":"https://api.github.com/repos/NuGet/NuGet.Client/commits{/sha}","git_commits_url":"https://api.github.com/repos/NuGet/NuGet.Client/git/commits{/sha}","comments_url":"https://api.github.com/repos/NuGet/NuGet.Client/comments{/number}","issue_comment_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues/comments{/number}","contents_url":"https://api.github.com/repos/NuGet/NuGet.Client/contents/{+path}","compare_url":"https://api.github.com/repos/NuGet/NuGet.Client/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NuGet/NuGet.Client/merges","archive_url":"https://api.github.com/repos/NuGet/NuGet.Client/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NuGet/NuGet.Client/downloads","issues_url":"https://api.github.com/repos/NuGet/NuGet.Client/issues{/number}","pulls_url":"https://api.github.com/repos/NuGet/NuGet.Client/pulls{/number}","milestones_url":"https://api.github.com/repos/NuGet/NuGet.Client/milestones{/number}","notifications_url":"https://api.github.com/repos/NuGet/NuGet.Client/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NuGet/NuGet.Client/labels{/name}","releases_url":"https://api.github.com/repos/NuGet/NuGet.Client/releases{/id}","deployments_url":"https://api.github.com/repos/NuGet/NuGet.Client/deployments","created_at":"2014-04-30T23:46:11Z","updated_at":"2016-12-13T12:14:05Z","pushed_at":"2016-12-17T01:15:17Z","git_url":"git://github.com/NuGet/NuGet.Client.git","ssh_url":"git@github.com:NuGet/NuGet.Client.git","clone_url":"https://github.com/NuGet/NuGet.Client.git","svn_url":"https://github.com/NuGet/NuGet.Client","homepage":null,"size":24441,"stargazers_count":127,"watchers_count":127,"language":"C#","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":164,"mirror_url":null,"open_issues_count":18,"forks":164,"open_issues":18,"watchers":127,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061"},"html":{"href":"https://github.com/NuGet/NuGet.Client/pull/1061"},"issue":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/issues/1061"},"comments":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/issues/1061/comments"},"review_comments":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061/comments"},"review_comment":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/pulls/1061/commits"},"statuses":{"href":"https://api.github.com/repos/NuGet/NuGet.Client/statuses/b2c4540ed77401bc1cd20f56b958826682e9380e"}}}}
{"actor":{"display_login":"alpaix"}}