Skip to content

Commit 70a2d16

Browse files
authored
Merge pull request #15600 from tamasvajk/buildless/no-mono-dlls
C# Change desktop dotnet assembly lookup to fall back to nuget reference assemblies
2 parents f2e04c0 + 216d6c0 commit 70a2d16

File tree

21 files changed

+227
-73
lines changed

21 files changed

+227
-73
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActio
7575
return defaultValue;
7676

7777
return value.
78-
Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).
78+
Split(FileUtils.NewLineCharacters, StringSplitOptions.RemoveEmptyEntries).
7979
Select(s => AsStringWithExpandedEnvVars(s, actions)).ToArray();
8080
}
8181

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

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
6262
try
6363
{
6464
this.dotnet = DotNet.Make(options, logger, tempWorkingDirectory);
65-
runtimeLazy = new Lazy<Runtime>(() => new Runtime(dotnet));
65+
runtimeLazy = new Lazy<Runtime>(() => new Runtime(dotnet, logger));
6666
}
6767
catch
6868
{
@@ -112,7 +112,7 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
112112
logger.LogInfo($"Unresolved reference {r.Key} in project {r.Value}");
113113
}
114114

115-
var webViewExtractionOption = Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_STANDALONE_EXTRACT_WEB_VIEWS");
115+
var webViewExtractionOption = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WebViewGeneration);
116116
if (webViewExtractionOption == null ||
117117
bool.TryParse(webViewExtractionOption, out var shouldExtractWebViews) &&
118118
shouldExtractWebViews)
@@ -159,6 +159,53 @@ private HashSet<string> AddFrameworkDlls(HashSet<string> dllPaths)
159159
{
160160
var frameworkLocations = new HashSet<string>();
161161

162+
var frameworkReferences = Environment.GetEnvironmentVariable(EnvironmentVariableNames.DotnetFrameworkReferences);
163+
var frameworkReferencesUseSubfolders = Environment.GetEnvironmentVariable(EnvironmentVariableNames.DotnetFrameworkReferencesUseSubfolders);
164+
_ = bool.TryParse(frameworkReferencesUseSubfolders, out var useSubfolders);
165+
if (!string.IsNullOrWhiteSpace(frameworkReferences))
166+
{
167+
RemoveFrameworkNugetPackages(dllPaths);
168+
RemoveNugetPackageReference(FrameworkPackageNames.AspNetCoreFramework, dllPaths);
169+
RemoveNugetPackageReference(FrameworkPackageNames.WindowsDesktopFramework, dllPaths);
170+
171+
var frameworkPaths = frameworkReferences.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries);
172+
173+
foreach (var path in frameworkPaths)
174+
{
175+
if (!Directory.Exists(path))
176+
{
177+
logger.LogError($"Specified framework reference path '{path}' does not exist.");
178+
continue;
179+
}
180+
181+
if (useSubfolders)
182+
{
183+
dllPaths.Add(path);
184+
frameworkLocations.Add(path);
185+
continue;
186+
}
187+
188+
try
189+
{
190+
var dlls = Directory.GetFiles(path, "*.dll", new EnumerationOptions { RecurseSubdirectories = false, MatchCasing = MatchCasing.CaseInsensitive });
191+
if (dlls.Length == 0)
192+
{
193+
logger.LogError($"No DLLs found in specified framework reference path '{path}'.");
194+
continue;
195+
}
196+
197+
dllPaths.UnionWith(dlls);
198+
frameworkLocations.UnionWith(dlls);
199+
}
200+
catch (Exception e)
201+
{
202+
logger.LogError($"Error while searching for DLLs in '{path}': {e.Message}");
203+
}
204+
}
205+
206+
return frameworkLocations;
207+
}
208+
162209
AddNetFrameworkDlls(dllPaths, frameworkLocations);
163210
AddAspNetCoreFrameworkDlls(dllPaths, frameworkLocations);
164211
AddMicrosoftWindowsDesktopDlls(dllPaths, frameworkLocations);
@@ -204,9 +251,9 @@ private void RestoreNugetPackages(List<FileInfo> allNonBinaryFiles, IEnumerable<
204251
nugetPackageDllPaths.ExceptWith(excludedPaths);
205252
dllPaths.UnionWith(nugetPackageDllPaths);
206253
}
207-
catch (Exception)
254+
catch (Exception exc)
208255
{
209-
logger.LogError("Failed to restore Nuget packages with nuget.exe");
256+
logger.LogError($"Failed to restore Nuget packages with nuget.exe: {exc.Message}");
210257
}
211258

212259
var restoredProjects = RestoreSolutions(allSolutions, out var assets1);
@@ -297,14 +344,23 @@ private static DirectoryInfo[] GetPackageVersionSubDirectories(string packagePat
297344
.ToArray();
298345
}
299346

347+
private void RemoveFrameworkNugetPackages(ISet<string> dllPaths, int fromIndex = 0)
348+
{
349+
var packagesInPrioOrder = FrameworkPackageNames.NetFrameworks;
350+
for (var i = fromIndex; i < packagesInPrioOrder.Length; i++)
351+
{
352+
RemoveNugetPackageReference(packagesInPrioOrder[i], dllPaths);
353+
}
354+
}
355+
300356
private void AddNetFrameworkDlls(ISet<string> dllPaths, ISet<string> frameworkLocations)
301357
{
302358
// Multiple dotnet framework packages could be present.
303359
// The order of the packages is important, we're adding the first one that is present in the nuget cache.
304360
var packagesInPrioOrder = FrameworkPackageNames.NetFrameworks;
305361

306362
var frameworkPaths = packagesInPrioOrder
307-
.Select((s, index) => (Index: index, Path: GetPackageDirectory(s)))
363+
.Select((s, index) => (Index: index, Path: GetPackageDirectory(s, packageDirectory)))
308364
.Where(pair => pair.Path is not null)
309365
.ToArray();
310366

@@ -318,12 +374,7 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths, ISet<string> frameworkLo
318374
}
319375

320376
SelectNewestFrameworkPath(frameworkPath.Path, ".NET Framework", dllPaths, frameworkLocations);
321-
322-
for (var i = frameworkPath.Index + 1; i < packagesInPrioOrder.Length; i++)
323-
{
324-
RemoveNugetPackageReference(packagesInPrioOrder[i], dllPaths);
325-
}
326-
377+
RemoveFrameworkNugetPackages(dllPaths, frameworkPath.Index + 1);
327378
return;
328379
}
329380

@@ -336,6 +387,16 @@ private void AddNetFrameworkDlls(ISet<string> dllPaths, ISet<string> frameworkLo
336387
else if (fileContent.IsLegacyProjectStructureUsed)
337388
{
338389
runtimeLocation = Runtime.DesktopRuntime;
390+
391+
if (runtimeLocation is null)
392+
{
393+
logger.LogInfo("No .NET Desktop Runtime location found. Attempting to restore the .NET Framework reference assemblies manually.");
394+
395+
if (TryRestorePackageManually(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies, null))
396+
{
397+
runtimeLocation = GetPackageDirectory(FrameworkPackageNames.LatestNetFrameworkReferenceAssemblies, missingPackageDirectory);
398+
}
399+
}
339400
}
340401

341402
runtimeLocation ??= Runtime.ExecutingRuntime;
@@ -375,7 +436,7 @@ private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths, ISet<string> fram
375436
}
376437

377438
// First try to find ASP.NET Core assemblies in the NuGet packages
378-
if (GetPackageDirectory(FrameworkPackageNames.AspNetCoreFramework) is string aspNetCorePackage)
439+
if (GetPackageDirectory(FrameworkPackageNames.AspNetCoreFramework, packageDirectory) is string aspNetCorePackage)
379440
{
380441
SelectNewestFrameworkPath(aspNetCorePackage, "ASP.NET Core", dllPaths, frameworkLocations);
381442
return;
@@ -391,15 +452,15 @@ private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths, ISet<string> fram
391452

392453
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths, ISet<string> frameworkLocations)
393454
{
394-
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework) is string windowsDesktopApp)
455+
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework, packageDirectory) is string windowsDesktopApp)
395456
{
396457
SelectNewestFrameworkPath(windowsDesktopApp, "Windows Desktop App", dllPaths, frameworkLocations);
397458
}
398459
}
399460

400-
private string? GetPackageDirectory(string packagePrefix)
461+
private string? GetPackageDirectory(string packagePrefix, TemporaryDirectory root)
401462
{
402-
return new DirectoryInfo(packageDirectory.DirInfo.FullName)
463+
return new DirectoryInfo(root.DirInfo.FullName)
403464
.EnumerateDirectories(packagePrefix + "*", new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive, RecurseSubdirectories = false })
404465
.FirstOrDefault()?
405466
.FullName;
@@ -435,19 +496,19 @@ private void GenerateSourceFileFromImplicitUsings()
435496
}
436497

437498
// Hardcoded values from https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview#implicit-using-directives
438-
usings.UnionWith(new[] { "System", "System.Collections.Generic", "System.IO", "System.Linq", "System.Net.Http", "System.Threading",
439-
"System.Threading.Tasks" });
499+
usings.UnionWith([ "System", "System.Collections.Generic", "System.IO", "System.Linq", "System.Net.Http", "System.Threading",
500+
"System.Threading.Tasks" ]);
440501

441502
if (fileContent.UseAspNetCoreDlls)
442503
{
443-
usings.UnionWith(new[] { "System.Net.Http.Json", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Hosting",
504+
usings.UnionWith([ "System.Net.Http.Json", "Microsoft.AspNetCore.Builder", "Microsoft.AspNetCore.Hosting",
444505
"Microsoft.AspNetCore.Http", "Microsoft.AspNetCore.Routing", "Microsoft.Extensions.Configuration",
445-
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.Hosting", "Microsoft.Extensions.Logging" });
506+
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.Hosting", "Microsoft.Extensions.Logging" ]);
446507
}
447508

448509
if (fileContent.UseWindowsForms)
449510
{
450-
usings.UnionWith(new[] { "System.Drawing", "System.Windows.Forms" });
511+
usings.UnionWith(["System.Drawing", "System.Windows.Forms"]);
451512
}
452513

453514
usings.UnionWith(fileContent.CustomImplicitUsings);
@@ -869,47 +930,58 @@ private void DownloadMissingPackages(List<FileInfo> allFiles, ISet<string> dllPa
869930

870931
Parallel.ForEach(notYetDownloadedPackages, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, package =>
871932
{
872-
logger.LogInfo($"Restoring package {package}...");
873-
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package, "missingpackages_workingdir"));
874-
var success = dotnet.New(tempDir.DirInfo.FullName);
933+
var success = TryRestorePackageManually(package, nugetConfig);
875934
if (!success)
876935
{
877936
return;
878937
}
879938

880-
success = dotnet.AddPackage(tempDir.DirInfo.FullName, package);
881-
if (!success)
939+
lock (sync)
882940
{
883-
return;
941+
successCount++;
884942
}
943+
});
885944

886-
var res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, PathToNugetConfig: nugetConfig));
887-
if (!res.Success)
888-
{
889-
if (res.HasNugetPackageSourceError)
890-
{
891-
// Restore could not be completed because the listed source is unavailable. Try without the nuget.config:
892-
res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, PathToNugetConfig: null, ForceReevaluation: true));
893-
}
945+
CompilationInfos.Add(("Successfully ran fallback nuget restore", successCount.ToString()));
894946

895-
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
947+
dllPaths.Add(missingPackageDirectory.DirInfo.FullName);
948+
}
896949

897-
if (!res.Success)
898-
{
899-
logger.LogInfo($"Failed to restore nuget package {package}");
900-
return;
901-
}
902-
}
950+
private bool TryRestorePackageManually(string package, string? nugetConfig)
951+
{
952+
logger.LogInfo($"Restoring package {package}...");
953+
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package, "missingpackages_workingdir"));
954+
var success = dotnet.New(tempDir.DirInfo.FullName);
955+
if (!success)
956+
{
957+
return false;
958+
}
903959

904-
lock (sync)
960+
success = dotnet.AddPackage(tempDir.DirInfo.FullName, package);
961+
if (!success)
962+
{
963+
return false;
964+
}
965+
966+
var res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, PathToNugetConfig: nugetConfig));
967+
if (!res.Success)
968+
{
969+
if (res.HasNugetPackageSourceError && nugetConfig is not null)
905970
{
906-
successCount++;
971+
// Restore could not be completed because the listed source is unavailable. Try without the nuget.config:
972+
res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, PathToNugetConfig: null, ForceReevaluation: true));
907973
}
908-
});
909974

910-
CompilationInfos.Add(("Successfully ran fallback nuget restore", successCount.ToString()));
975+
// TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package.
911976

912-
dllPaths.Add(missingPackageDirectory.DirInfo.FullName);
977+
if (!res.Success)
978+
{
979+
logger.LogInfo($"Failed to restore nuget package {package}");
980+
return false;
981+
}
982+
}
983+
984+
return true;
913985
}
914986

915987
public void Dispose(TemporaryDirectory? dir, string name)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Semmle.Extraction.CSharp.DependencyFetching
2+
{
3+
internal class EnvironmentVariableNames
4+
{
5+
/// <summary>
6+
/// Controls whether to generate source files from Asp.Net Core views (`.cshtml`, `.razor`).
7+
/// </summary>
8+
public const string WebViewGeneration = "CODEQL_EXTRACTOR_CSHARP_BUILDLESS_EXTRACT_WEB_VIEWS";
9+
10+
/// <summary>
11+
/// Specifies the location of .Net framework references added to the compilation.
12+
/// </summary>
13+
public const string DotnetFrameworkReferences = "CODEQL_EXTRACTOR_CSHARP_BUILDLESS_DOTNET_FRAMEWORK_REFERENCES";
14+
15+
/// <summary>
16+
/// Controls whether to use framework dependencies from subfolders.
17+
/// </summary>
18+
public const string DotnetFrameworkReferencesUseSubfolders = "CODEQL_EXTRACTOR_CSHARP_BUILDLESS_DOTNET_FRAMEWORK_REFERENCES_USE_SUBFOLDERS";
19+
}
20+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private record class PathFilter(Regex Regex, bool Include);
3131

3232
public IEnumerable<FileInfo> Filter(IEnumerable<FileInfo> files)
3333
{
34-
var filters = (Environment.GetEnvironmentVariable("LGTM_INDEX_FILTERS") ?? string.Empty).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
34+
var filters = (Environment.GetEnvironmentVariable("LGTM_INDEX_FILTERS") ?? string.Empty).Split(FileUtils.NewLineCharacters, StringSplitOptions.RemoveEmptyEntries);
3535
if (filters.Length == 0)
3636
{
3737
return files;
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32

43
namespace Semmle.Extraction.CSharp.DependencyFetching
54
{
65
internal static class FrameworkPackageNames
76
{
7+
public static string LatestNetFrameworkReferenceAssemblies { get; } = "microsoft.netframework.referenceassemblies.net481";
8+
89
public static string AspNetCoreFramework { get; } = "microsoft.aspnetcore.app.ref";
910

1011
public static string WindowsDesktopFramework { get; } = "microsoft.windowsdesktop.app.ref";
1112

1213
// The order of the packages is important.
13-
public static string[] NetFrameworks { get; } = new string[]
14-
{
14+
public static string[] NetFrameworks { get; } =
15+
[
1516
"microsoft.netcore.app.ref", // net7.0, ... net5.0, netcoreapp3.1, netcoreapp3.0
1617
"microsoft.netframework.referenceassemblies.", // net48, ..., net20
1718
"netstandard.library.ref", // netstandard2.1
1819
"netstandard.library" // netstandard2.0
19-
};
20+
];
2021

2122
public static IEnumerable<string> AllFrameworks { get; } =
22-
NetFrameworks
23-
.Union(new string[] { AspNetCoreFramework, WindowsDesktopFramework });
23+
[.. NetFrameworks, AspNetCoreFramework, WindowsDesktopFramework];
2424
}
2525
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public IEnumerable<string> GenerateFiles(IEnumerable<string> cshtmls, IEnumerabl
7171
var args = new StringBuilder();
7272
args.Append($"/target:exe /generatedfilesout:\"{outputFolder}\" /out:\"{dllPath}\" /analyzerconfig:\"{analyzerConfig}\" ");
7373

74-
foreach (var f in Directory.GetFiles(sourceGeneratorFolder, "*.dll"))
74+
foreach (var f in Directory.GetFiles(sourceGeneratorFolder, "*.dll", new EnumerationOptions { RecurseSubdirectories = false, MatchCasing = MatchCasing.CaseInsensitive }))
7575
{
7676
args.Append($"/analyzer:\"{f}\" ");
7777
}

0 commit comments

Comments
 (0)