Skip to content

Instantly share code, notes, and snippets.

@andersstorhaug
Last active July 15, 2021 16:00
Show Gist options
  • Select an option

  • Save andersstorhaug/c7460c7e754f343ae1ad2c6fd8dba051 to your computer and use it in GitHub Desktop.

Select an option

Save andersstorhaug/c7460c7e754f343ae1ad2c6fd8dba051 to your computer and use it in GitHub Desktop.
Roslyn scripting with NuGet references
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dotnet.Script.DependencyModel.Environment;
using Dotnet.Script.DependencyModel.Logging;
using Dotnet.Script.DependencyModel.ProjectSystem;
using Microsoft.CodeAnalysis;
using NuGet.Commands;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.ProjectModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.RuntimeModel;
using NuGet.Versioning;
namespace ScriptingSpike
{
public class NugetReferencesResolver
{
private readonly LogFactory _logFactory;
private readonly ScriptFilesResolver _scriptFilesResolver;
private readonly ScriptParser _scriptParser;
public NugetReferencesResolver(
LogFactory logFactory,
ScriptParser scriptParser = null,
ScriptFilesResolver scriptFilesResolver = null)
{
_logFactory = logFactory;
_scriptParser = scriptParser ?? new ScriptParser(logFactory);
_scriptFilesResolver = scriptFilesResolver ?? new ScriptFilesResolver();
}
public async IAsyncEnumerable<MetadataReference> GetDependencies(string scriptFile, string[] packageSources)
{
var framework = NuGetFramework.Parse(ScriptEnvironment.Default.TargetFramework);
var runtimeIdentifier = ScriptEnvironment.Default.RuntimeIdentifier;
var scriptDirectory = Path.GetDirectoryName(scriptFile) ?? throw new ArgumentException("Must be a valid path", nameof(scriptFile));
// Performs the standard `NuGet.Config` lookup
var settings = Settings.LoadDefaultSettings(scriptDirectory);
var sourceRepositories = SettingsUtility
.GetEnabledSources(settings)
.Select(value => Repository.Factory.GetCoreV3(value.Source))
.Concat(packageSources.Select(source => Repository.Factory.GetCoreV3(source)))
.ToList();
var logger = new NuGetLogger(_logFactory);
using var sourceCacheContext = new SourceCacheContext();
var providers = RestoreCommandProviders.Create(
SettingsUtility.GetGlobalPackagesFolder(settings),
fallbackPackageFolderPaths: Enumerable.Empty<string>(),
sourceRepositories,
sourceCacheContext,
new LocalPackageFileCache(),
logger);
var parseResult = _scriptParser.ParseFromFiles(_scriptFilesResolver.GetScriptFiles(scriptFile));
var packageSpec = new PackageSpec
{
FilePath = Path.Combine(scriptDirectory, "script.json"),
Name = "script",
Version = new NuGetVersion(1, 0, 0),
TargetFrameworks =
{
new TargetFrameworkInformation
{
FrameworkName = framework,
Dependencies = parseResult.PackageReferences.Select(package => new LibraryDependency
{
LibraryRange = new LibraryRange(
package.Id.Value,
VersionRange.Parse(package.Version.Value),
LibraryDependencyTarget.Package)
}).ToList()
}
},
RuntimeGraph = new RuntimeGraph(new[]
{
new RuntimeDescription(runtimeIdentifier)
})
};
var command = new RestoreCommand(new RestoreRequest(packageSpec, providers, sourceCacheContext, null, logger));
// Note, the package file is not created, because `RestoreResult.CommitAsync` is not called
var result = await command.ExecuteAsync();
var lockFile = result.LockFile;
var target = lockFile.GetTarget(framework, runtimeIdentifier);
// N.B. Native assemblies are not handled here, but, theoretically could be
var paths = target.Libraries
.SelectMany(library => library.CompileTimeAssemblies
.Concat(library.RuntimeAssemblies)
.Select(item => (library.Name, library.Version, item.Path)))
.Where(value => Path.GetExtension(value.Path) == ".dll")
.SelectMany(
_ => lockFile.PackageFolders.Select(folder => folder.Path),
(library, folder) => Path.Combine(folder, library.Name, library.Version.ToString(), library.Path))
.Where(File.Exists)
.ToList();
foreach (var dependency in paths)
{
yield return MetadataReference.CreateFromFile(dependency);
}
}
}
}
@andersstorhaug
Copy link
Copy Markdown
Author

This uses a reference to the NuGet.Commands NuGet package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment