Skip to content

Commit 5f4861f

Browse files
authored
Merge pull request github#14069 from michaelnebel/csharp/nugetexe
C#: Download `nuget.exe` in the dependency manager (if not present).
2 parents 0d1fd88 + 5de8d91 commit 5f4861f

File tree

5 files changed

+102
-62
lines changed

5 files changed

+102
-62
lines changed

csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
using Semmle.Util;
2-
using System;
1+
using System;
32
using System.Collections.Generic;
43
using System.Diagnostics;
5-
using System.IO;
6-
using System.Xml;
7-
using System.Net.Http;
84
using System.Diagnostics.CodeAnalysis;
9-
using System.Threading.Tasks;
5+
using System.IO;
106
using System.Runtime.InteropServices;
7+
using System.Xml;
8+
using Semmle.Util;
119

1210
namespace Semmle.Autobuild.Shared
1311
{
@@ -283,17 +281,8 @@ XmlDocument IBuildActions.LoadXml(string filename)
283281

284282
public string EnvironmentExpandEnvironmentVariables(string s) => Environment.ExpandEnvironmentVariables(s);
285283

286-
private static async Task DownloadFileAsync(string address, string filename)
287-
{
288-
using var httpClient = new HttpClient();
289-
using var request = new HttpRequestMessage(HttpMethod.Get, address);
290-
using var contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync();
291-
using var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
292-
await contentStream.CopyToAsync(stream);
293-
}
294-
295284
public void DownloadFile(string address, string fileName) =>
296-
DownloadFileAsync(address, fileName).Wait();
285+
FileUtils.DownloadFile(address, fileName);
297286

298287
public IDiagnosticsWriter CreateDiagnosticsWriter(string filename) => new DiagnosticsStream(filename);
299288

csharp/autobuilder/Semmle.Autobuild.Shared/MsBuildRule.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using Semmle.Util.Logging;
21
using System.Collections.Generic;
32
using System.Linq;
3+
using Semmle.Util;
4+
using Semmle.Util.Logging;
45

56
namespace Semmle.Autobuild.Shared
67
{
@@ -190,7 +191,7 @@ private static BuildScript DownloadNugetExe<TAutobuildOptions>(IAutobuilder<TAut
190191
})
191192
&
192193
BuildScript.DownloadFile(
193-
"https://dist.nuget.org/win-x86-commandline/latest/nuget.exe",
194+
FileUtils.NugetExeUrl,
194195
path,
195196
e => builder.Log(Severity.Warning, $"Failed to download 'nuget.exe': {e.Message}"))
196197
&
Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Diagnostics;
43
using System.IO;
54
using System.Linq;
@@ -14,70 +13,91 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
1413
/// </summary>
1514
internal class NugetPackages
1615
{
16+
private readonly string nugetExe;
17+
private readonly ProgressMonitor progressMonitor;
18+
19+
/// <summary>
20+
/// The list of package files.
21+
/// </summary>
22+
private readonly FileInfo[] packageFiles;
23+
24+
/// <summary>
25+
/// The computed packages directory.
26+
/// This will be in the Temp location
27+
/// so as to not trample the source tree.
28+
/// </summary>
29+
private readonly TemporaryDirectory packageDirectory;
30+
1731
/// <summary>
1832
/// Create the package manager for a specified source tree.
1933
/// </summary>
2034
public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor)
2135
{
22-
SourceDirectory = sourceDir;
23-
PackageDirectory = packageDirectory;
36+
this.packageDirectory = packageDirectory;
2437
this.progressMonitor = progressMonitor;
2538

26-
// Expect nuget.exe to be in a `nuget` directory under the directory containing this exe.
27-
var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
28-
var directory = Path.GetDirectoryName(currentAssembly)
29-
?? throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null");
30-
nugetExe = Path.Combine(directory, "nuget", "nuget.exe");
31-
32-
if (!File.Exists(nugetExe))
33-
throw new FileNotFoundException(string.Format("NuGet could not be found at {0}", nugetExe));
34-
35-
packages = new DirectoryInfo(SourceDirectory)
39+
nugetExe = ResolveNugetExe(sourceDir);
40+
packageFiles = new DirectoryInfo(sourceDir)
3641
.EnumerateFiles("packages.config", SearchOption.AllDirectories)
3742
.ToArray();
3843
}
3944

40-
// List of package files to download.
41-
private readonly FileInfo[] packages;
42-
43-
/// <summary>
44-
/// The list of package files.
45-
/// </summary>
46-
public IEnumerable<FileInfo> PackageFiles => packages;
47-
4845
/// <summary>
49-
/// Download the packages to the temp folder.
46+
/// Tries to find the location of `nuget.exe` in the nuget directory under the directory
47+
/// containing the executing assembly. If it can't be found, it is downloaded to the
48+
/// `.nuget` directory under the source directory.
5049
/// </summary>
51-
/// <param name="pm">The progress monitor used for reporting errors etc.</param>
52-
public void InstallPackages()
50+
/// <param name="sourceDir">The source directory.</param>
51+
private string ResolveNugetExe(string sourceDir)
5352
{
54-
foreach (var package in packages)
53+
var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
54+
var directory = Path.GetDirectoryName(currentAssembly)
55+
?? throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null");
56+
57+
var nuget = Path.Combine(directory, "nuget", "nuget.exe");
58+
if (File.Exists(nuget))
5559
{
56-
RestoreNugetPackage(package.FullName);
60+
progressMonitor.FoundNuGet(nuget);
61+
return nuget;
62+
}
63+
else
64+
{
65+
progressMonitor.LogInfo($"Nuget.exe could not be found at {nuget}");
66+
return DownloadNugetExe(sourceDir);
5767
}
5868
}
5969

60-
/// <summary>
61-
/// The source directory used.
62-
/// </summary>
63-
public string SourceDirectory
70+
private string DownloadNugetExe(string sourceDir)
6471
{
65-
get;
66-
private set;
67-
}
72+
var directory = Path.Combine(sourceDir, ".nuget");
73+
var nuget = Path.Combine(directory, "nuget.exe");
6874

69-
/// <summary>
70-
/// The computed packages directory.
71-
/// This will be in the Temp location
72-
/// so as to not trample the source tree.
73-
/// </summary>
74-
public TemporaryDirectory PackageDirectory { get; }
75+
// Nuget.exe already exists in the .nuget directory.
76+
if (File.Exists(nuget))
77+
{
78+
progressMonitor.FoundNuGet(nuget);
79+
return nuget;
80+
}
81+
82+
Directory.CreateDirectory(directory);
83+
progressMonitor.LogInfo("Attempting to download nuget.exe");
84+
try
85+
{
86+
FileUtils.DownloadFile(FileUtils.NugetExeUrl, nuget);
87+
progressMonitor.LogInfo($"Downloaded nuget.exe to {nuget}");
88+
return nuget;
89+
}
90+
catch
91+
{
92+
// Download failed.
93+
throw new FileNotFoundException("Download of nuget.exe failed.");
94+
}
95+
}
7596

7697
/// <summary>
7798
/// Restore all files in a specified package.
7899
/// </summary>
79100
/// <param name="package">The package file.</param>
80-
/// <param name="pm">Where to log progress/errors.</param>
81101
private void RestoreNugetPackage(string package)
82102
{
83103
progressMonitor.NugetInstall(package);
@@ -92,12 +112,12 @@ private void RestoreNugetPackage(string package)
92112
if (Util.Win32.IsWindows())
93113
{
94114
exe = nugetExe;
95-
args = string.Format("install -OutputDirectory {0} {1}", PackageDirectory, package);
115+
args = string.Format("install -OutputDirectory {0} {1}", packageDirectory, package);
96116
}
97117
else
98118
{
99119
exe = "mono";
100-
args = string.Format("{0} install -OutputDirectory {1} {2}", nugetExe, PackageDirectory, package);
120+
args = string.Format("{0} install -OutputDirectory {1} {2}", nugetExe, packageDirectory, package);
101121
}
102122

103123
var pi = new ProcessStartInfo(exe, args)
@@ -133,7 +153,15 @@ private void RestoreNugetPackage(string package)
133153
}
134154
}
135155

136-
private readonly string nugetExe;
137-
private readonly ProgressMonitor progressMonitor;
156+
/// <summary>
157+
/// Download the packages to the temp folder.
158+
/// </summary>
159+
public void InstallPackages()
160+
{
161+
foreach (var package in packageFiles)
162+
{
163+
RestoreNugetPackage(package.FullName);
164+
}
165+
}
138166
}
139167
}

csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/ProgressMonitor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ public void CommandFailed(string exe, string arguments, int exitCode) =>
8888
public void MissingNuGet() =>
8989
LogError("Missing nuget.exe");
9090

91+
public void FoundNuGet(string path) =>
92+
LogInfo($"Found nuget.exe at {path}");
93+
9194
public void MissingDotNet() =>
9295
LogError("Missing dotnet CLI");
9396

csharp/extractor/Semmle.Util/FileUtils.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
using System;
22
using System.IO;
33
using System.Linq;
4+
using System.Net.Http;
45
using System.Security.Cryptography;
56
using System.Text;
7+
using System.Threading.Tasks;
68

79
namespace Semmle.Util
810
{
911
public static class FileUtils
1012
{
13+
public const string NugetExeUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe";
14+
1115
public static string ConvertToWindows(string path)
1216
{
1317
return path.Replace('/', '\\');
@@ -91,5 +95,20 @@ public static string ComputeFileHash(string filePath)
9195
hex.AppendFormat("{0:x2}", b);
9296
return hex.ToString();
9397
}
98+
99+
private static async Task DownloadFileAsync(string address, string filename)
100+
{
101+
using var httpClient = new HttpClient();
102+
using var request = new HttpRequestMessage(HttpMethod.Get, address);
103+
using var contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync();
104+
using var stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
105+
await contentStream.CopyToAsync(stream);
106+
}
107+
108+
/// <summary>
109+
/// Downloads the file at <paramref name="address"/> to <paramref name="fileName"/>.
110+
/// </summary>
111+
public static void DownloadFile(string address, string fileName) =>
112+
DownloadFileAsync(address, fileName).Wait();
94113
}
95114
}

0 commit comments

Comments
 (0)