Skip to content

Commit a2e2faf

Browse files
committed
Added initial support for Microsoft.NET.Sdk.Web
1 parent fc932e5 commit a2e2faf

File tree

12 files changed

+175
-21
lines changed

12 files changed

+175
-21
lines changed

src/.vscode/tasks.json

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,34 @@
66
"command": "dotnet",
77
"args": [
88
"build",
9-
"${workspaceRoot}/Dotnet.Script/Dotnet.Script.csproj",
109
"/property:GenerateFullPaths=true"
1110
],
12-
"group": {
13-
"kind": "build",
14-
"isDefault": true
11+
"options": {
12+
"cwd": "${workspaceFolder}/.."
13+
},
14+
"type": "shell",
15+
"group": "build",
16+
"presentation": {
17+
"reveal": "always"
18+
},
19+
"problemMatcher": "$msCompile"
20+
},
21+
{
22+
"label": "rebuild",
23+
"command": "dotnet",
24+
"args": [
25+
"build",
26+
"--no-incremental",
27+
"/property:GenerateFullPaths=true"
28+
],
29+
"options": {
30+
"cwd": "${workspaceFolder}/.."
31+
},
32+
"type": "shell",
33+
"group": "build",
34+
"presentation": {
35+
"reveal": "always",
36+
"clear": true
1537
},
1638
"problemMatcher": "$msCompile"
1739
},

src/Dotnet.Script.DependencyModel.Nuget/NuGetMetadataReferenceResolver.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public NuGetMetadataReferenceResolver(MetadataReferenceResolver metadataReferenc
2121
{
2222
_metadataReferenceResolver = metadataReferenceResolver;
2323
}
24-
24+
2525
public override bool Equals(object other)
2626
{
2727
return _metadataReferenceResolver.Equals(other);
@@ -42,7 +42,7 @@ public override PortableExecutableReference ResolveMissingAssembly(MetadataRefer
4242

4343
public override ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties)
4444
{
45-
if (reference.StartsWith("nuget", StringComparison.OrdinalIgnoreCase))
45+
if (reference.StartsWith("nuget", StringComparison.OrdinalIgnoreCase) || reference.StartsWith("sdk", StringComparison.OrdinalIgnoreCase))
4646
{
4747
// HACK We need to return something here to "mark" the reference as resolved.
4848
// https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs#L838

src/Dotnet.Script.DependencyModel/Context/ScriptDependencyContextReader.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using System.Linq;
55
using System.Reflection;
66
using System.Threading.Tasks;
7+
using System.Xml.Linq;
78
using Dotnet.Script.DependencyModel.Environment;
89
using Dotnet.Script.DependencyModel.Logging;
10+
using Dotnet.Script.DependencyModel.ProjectSystem;
911
using Dotnet.Script.DependencyModel.ScriptPackage;
1012
using Microsoft.DotNet.PlatformAbstractions;
1113
using NuGet.Common;
@@ -36,6 +38,10 @@ public ScriptDependencyContextReader(LogFactory logFactory)
3638

3739
public ScriptDependencyContext ReadDependencyContext(string pathToAssetsFile)
3840
{
41+
var pathToProjectFile = GetPathToProjectFile(pathToAssetsFile);
42+
var projectFile = XDocument.Load(pathToProjectFile);
43+
var sdk = projectFile.Descendants("Project").Single().Attributes("Sdk").Single().Value;
44+
3945
var lockFile = GetLockFile(pathToAssetsFile);
4046
// Since we execute "dotnet restore -r [rid]" we get two targets in the lock file.
4147
// The second target is the one containing the runtime deps for the given RID.
@@ -66,10 +72,66 @@ public ScriptDependencyContext ReadDependencyContext(string pathToAssetsFile)
6672
var netcoreAppRuntimeAssemblies = Directory.GetFiles(netcoreAppRuntimeAssemblyLocation, "*.dll").Where(IsAssembly).ToArray();
6773
var netCoreAppDependency = new ScriptDependency("Microsoft.NETCore.App", ScriptEnvironment.Default.NetCoreVersion.Version, netcoreAppRuntimeAssemblies, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>());
6874
scriptDependencies.Add(netCoreAppDependency);
75+
if (sdk == "Microsoft.NET.Sdk.Web")
76+
{
77+
var aspNetCoreRuntimeInfo = GetAspNetCoreRuntimeInfo(netcoreAppRuntimeAssemblyLocation);
78+
var aspNetCoreAppRuntimeAssemblies = Directory.GetFiles(aspNetCoreRuntimeInfo.aspNetCoreRuntimeAssemblyLocation, "*.dll").Where(IsAssembly).ToArray();
79+
var aspNetCoreAppDependency = new ScriptDependency("Microsoft.AspNetCore.App", aspNetCoreRuntimeInfo.aspNetCoreVersion, aspNetCoreAppRuntimeAssemblies, Array.Empty<string>(), Array.Empty<string>(), Array.Empty<string>());
80+
scriptDependencies.Add(aspNetCoreAppDependency);
81+
}
6982
}
7083
return new ScriptDependencyContext(scriptDependencies.ToArray());
7184
}
7285

86+
private static string GetPathToProjectFile(string pathToAssetsFile)
87+
{
88+
var pathToProjectFile = Directory.GetFiles(Path.Combine(Path.GetDirectoryName(pathToAssetsFile), ".."), "*.csproj", SearchOption.TopDirectoryOnly).SingleOrDefault();
89+
if (pathToProjectFile is null)
90+
{
91+
pathToProjectFile = Directory.GetFiles(Path.Combine(Path.GetDirectoryName(pathToAssetsFile), "..", "..", "..", ScriptEnvironment.Default.TargetFramework), "*.csproj", SearchOption.TopDirectoryOnly).SingleOrDefault();
92+
}
93+
94+
if (pathToProjectFile is null)
95+
{
96+
throw new InvalidOperationException($"Unable to locate project file based on {pathToAssetsFile}");
97+
}
98+
99+
return pathToProjectFile;
100+
}
101+
102+
private static (string aspNetCoreRuntimeAssemblyLocation, string aspNetCoreVersion) GetAspNetCoreRuntimeInfo(string netcoreAppRuntimeAssemblyLocation)
103+
{
104+
var netCoreAppRuntimeVersion = Path.GetFileName(netcoreAppRuntimeAssemblyLocation);
105+
if (!SemanticVersion.TryParse(netCoreAppRuntimeVersion, out var version))
106+
{
107+
throw new InvalidOperationException("Unable to parse version");
108+
}
109+
var pathToSharedFolder = Path.GetFullPath(Path.Combine(netcoreAppRuntimeAssemblyLocation, "..", ".."));
110+
111+
//Microsoft.AspNetCore.App
112+
var pathToAspNetCoreRuntimeFolder = Directory.GetDirectories(pathToSharedFolder, "Microsoft.AspNetCore.App", SearchOption.TopDirectoryOnly).Single();
113+
114+
var aspNetCoreVersionsFolders = Directory.GetDirectories(pathToAspNetCoreRuntimeFolder).Select(folder => Path.GetFileName(folder));
115+
116+
var aspNetCoreVersions = new List<SemanticVersion>();
117+
foreach (var aspNetCoreVersionsFolder in aspNetCoreVersionsFolders)
118+
{
119+
if (!SemanticVersion.TryParse(aspNetCoreVersionsFolder, out var aspNetCoreVersion))
120+
{
121+
throw new InvalidOperationException("Unable to parse version");
122+
}
123+
else
124+
{
125+
aspNetCoreVersions.Add(aspNetCoreVersion);
126+
}
127+
}
128+
129+
var latestAspNetCoreVersion = aspNetCoreVersions.Where(v => v.Major == version.Major).OrderBy(v => v).Last();
130+
131+
return (Path.Combine(pathToAspNetCoreRuntimeFolder, latestAspNetCoreVersion.ToNormalizedString()), latestAspNetCoreVersion.ToNormalizedString());
132+
}
133+
134+
73135
private static bool IsAssembly(string file)
74136
{
75137
// https://docs.microsoft.com/en-us/dotnet/standard/assembly/identify

src/Dotnet.Script.DependencyModel/ProjectSystem/ParseResult.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ public ParseResult(IReadOnlyCollection<PackageReference> packageReferences)
1010
}
1111

1212
public IReadOnlyCollection<PackageReference> PackageReferences { get; }
13+
14+
public string Sdk { get; set; }
1315
}
1416
}

src/Dotnet.Script.DependencyModel/ProjectSystem/ProjectFile.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public ProjectFile(string xmlContent)
3939
{
4040
AssemblyReferences.Add(new AssemblyReference(assemblyReference.Attribute("Include").Value));
4141
}
42+
43+
Sdk = projectFileDocument.Descendants("Project").Single().Attributes("Sdk").Single().Value;
4244
}
4345

4446
/// <summary>
@@ -61,9 +63,20 @@ public ProjectFile(string xmlContent)
6163
/// </summary>
6264
public string TargetFramework { get; set; } = ScriptEnvironment.Default.TargetFramework;
6365

66+
/// <summary>
67+
/// Gets the project SDK
68+
/// </summary>
69+
public string Sdk { get; set; }
70+
6471
public void Save(string pathToProjectFile)
6572
{
6673
var projectFileDocument = XDocument.Parse(ReadTemplate("csproj.template"));
74+
if (!string.IsNullOrEmpty(Sdk))
75+
{
76+
var projectElement = projectFileDocument.Descendants("Project").Single();
77+
projectElement.Attributes("Sdk").Single().Value = Sdk;
78+
}
79+
6780
var itemGroupElement = projectFileDocument.Descendants("ItemGroup").Single();
6881
foreach (var packageReference in PackageReferences)
6982
{
@@ -101,7 +114,8 @@ public bool Equals(ProjectFile other)
101114
if (ReferenceEquals(this, other)) return true;
102115
return PackageReferences.SequenceEqual(other.PackageReferences)
103116
&& AssemblyReferences.SequenceEqual(other.AssemblyReferences)
104-
&& TargetFramework.Equals(other.TargetFramework);
117+
&& TargetFramework.Equals(other.TargetFramework)
118+
&& Sdk.Equals(other.Sdk);
105119
}
106120

107121
/// <inheritdoc/>

src/Dotnet.Script.DependencyModel/ProjectSystem/ScriptParser.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,39 @@ public ParseResult ParseFromCode(string code)
2626
public ParseResult ParseFromFiles(IEnumerable<string> csxFiles)
2727
{
2828
var allPackageReferences = new HashSet<PackageReference>();
29+
string sdk = string.Empty;
2930
foreach (var csxFile in csxFiles)
3031
{
3132
_logger.Debug($"Parsing {csxFile}");
3233
var fileContent = File.ReadAllText(csxFile);
3334
allPackageReferences.UnionWith(ReadPackageReferencesFromReferenceDirective(fileContent));
3435
allPackageReferences.UnionWith(ReadPackageReferencesFromLoadDirective(fileContent));
36+
var sdkReference = ReadSdkFromReferenceDirective(fileContent);
37+
if (!string.IsNullOrWhiteSpace(sdkReference))
38+
{
39+
sdk = sdkReference;
40+
}
3541
}
3642

37-
return new ParseResult(allPackageReferences);
43+
return new ParseResult(allPackageReferences) { Sdk = sdk };
44+
}
45+
46+
private static string ReadSdkFromReferenceDirective(string fileContent)
47+
{
48+
const string pattern = DirectivePatternPrefix + "r" + SdkDirectivePatternSuffix;
49+
var match = Regex.Match(fileContent, pattern);
50+
return match.Success ? match.Groups[1].Value : string.Empty;
3851
}
3952

4053
private static IEnumerable<PackageReference> ReadPackageReferencesFromReferenceDirective(string fileContent)
4154
{
42-
const string pattern = DirectivePatternPrefix + "r" + DirectivePatternSuffix;
55+
const string pattern = DirectivePatternPrefix + "r" + NuGetDirectivePatternSuffix;
4356
return ReadPackageReferencesFromDirective(pattern, fileContent);
4457
}
4558

4659
private static IEnumerable<PackageReference> ReadPackageReferencesFromLoadDirective(string fileContent)
4760
{
48-
const string pattern = DirectivePatternPrefix + "load" + DirectivePatternSuffix;
61+
const string pattern = DirectivePatternPrefix + "load" + NuGetDirectivePatternSuffix;
4962
return ReadPackageReferencesFromDirective(pattern, fileContent);
5063
}
5164

src/Dotnet.Script.DependencyModel/ProjectSystem/ScriptParserInternal.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@ partial class ScriptParser
77
const string Hws = @"[\x20\t]*"; // hws = horizontal whitespace
88

99
const string NuGetPattern = @"nuget:"
10-
// https://github.com/NuGet/docs.microsoft.com-nuget/issues/543#issue-270039223
10+
// https://github.com/NuGet/docs.microsoft.com-nuget/issues/543#issue-270039223
1111
+ Hws + @"(\w+(?:[_.-]\w+)*)"
1212
+ @"(?:" + Hws + "," + Hws + @"(.+?))?";
1313

14+
const string SdkPattern = @"sdk:"
15+
+ Hws + @"(\w+(?:[_.-]\w+)*)"
16+
+ @"(?:" + Hws + @")?";
17+
1418
const string WholeNuGetPattern = @"^" + NuGetPattern + @"$";
1519

1620
const string DirectivePatternPrefix = @"^" + Hws + @"#";
17-
const string DirectivePatternSuffix = Hws + @"""" + NuGetPattern + @"""";
21+
const string NuGetDirectivePatternSuffix = Hws + @"""" + NuGetPattern + @"""";
22+
23+
const string SdkDirectivePatternSuffix = Hws + @"""" + SdkPattern + @"""";
1824

1925
internal static bool TryParseNuGetPackageReference(string input,
2026
out string id, out string version)
2127
{
2228
bool success;
2329
(success, id, version) =
2430
Regex.Match(input, WholeNuGetPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)
25-
is {} match && match.Success
31+
is { } match && match.Success
2632
? (true, match.Groups[1].Value, match.Groups[2].Value)
2733
: default;
2834
return success;

src/Dotnet.Script.DependencyModel/ProjectSystem/ScriptProjectProvider.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.IO;
43
using System.Linq;
5-
using System.Text.RegularExpressions;
64
using Dotnet.Script.DependencyModel.Environment;
75
using Dotnet.Script.DependencyModel.Logging;
8-
using Dotnet.Script.DependencyModel.Process;
96

107
namespace Dotnet.Script.DependencyModel.ProjectSystem
118
{
@@ -57,6 +54,7 @@ public ProjectFileInfo CreateProjectForRepl(string code, string targetDirectory,
5754
}
5855

5956
projectFile.TargetFramework = defaultTargetFramework;
57+
projectFile.Sdk = parseResultFromCode.Sdk;
6058

6159
projectFile.Save(pathToProjectFile);
6260

@@ -116,16 +114,17 @@ private ProjectFileInfo SaveProjectFileFromScriptFiles(string targetDirectory, s
116114

117115
public ProjectFile CreateProjectFileFromScriptFiles(string defaultTargetFramework, string[] csxFiles)
118116
{
119-
var parseresult = _scriptParser.ParseFromFiles(csxFiles);
117+
var parseResult = _scriptParser.ParseFromFiles(csxFiles);
120118

121119
var projectFile = new ProjectFile();
122120

123-
foreach (var packageReference in parseresult.PackageReferences)
121+
foreach (var packageReference in parseResult.PackageReferences)
124122
{
125123
projectFile.PackageReferences.Add(packageReference);
126124
}
127125

128126
projectFile.TargetFramework = defaultTargetFramework;
127+
projectFile.Sdk = parseResult.Sdk;
129128
return projectFile;
130129
}
131130

src/Dotnet.Script.Tests/CompilationDependencyResolverTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ public void ShouldGetCompilationDependenciesForIssue129()
7272
Assert.Contains(dependencies, d => d.Name == "Auth0.ManagementApi");
7373
}
7474

75+
[Fact]
76+
public void ShouldGetCompilationDependenciesForWebSdk()
77+
{
78+
var resolver = CreateResolver();
79+
var targetDirectory = TestPathUtils.GetPathToTestFixtureFolder("WebApi");
80+
var csxFiles = Directory.GetFiles(targetDirectory, "*.csx");
81+
var dependencies = resolver.GetDependencies(targetDirectory, csxFiles, true, _scriptEnvironment.TargetFramework);
82+
Assert.Contains(dependencies.SelectMany(d => d.AssemblyPaths), p => p.Contains("Microsoft.AspNetCore.Components"));
83+
}
84+
7585
private CompilationDependencyResolver CreateResolver()
7686
{
7787
var resolver = new CompilationDependencyResolver(TestOutputHelper.CreateTestLogFactory());

src/Dotnet.Script.Tests/ScriptExecutionTests.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ public ScriptExecutionTests(ITestOutputHelper testOutputHelper)
2121
[Fact]
2222
public void ShouldExecuteHelloWorld()
2323
{
24-
var (output, _) = ScriptTestRunner.Default.ExecuteFixture("HelloWorld", "--no-cache");
25-
Assert.Contains("Hello World", output);
24+
ScriptTestRunner.ExecuteFixtureInProcess("HelloWorld", "--no-cache");
25+
// var (output, _) = ScriptTestRunner.Default.ExecuteFixture("HelloWorld", "--no-cache");
26+
// Assert.Contains("Hello World", output);
2627
}
2728

2829
[Fact]
@@ -479,6 +480,15 @@ public void ShouldSetCurrentContextualReflectionContext()
479480
Assert.Contains("Dotnet.Script.Core.ScriptAssemblyLoadContext", output);
480481
}
481482

483+
[Fact]
484+
public void ShouldCompileAndExecuteWithWebSdk()
485+
{
486+
var test = ScriptTestRunner.ExecuteFixtureInProcess("WebApi", "--no-cache --isolated-load-context");
487+
488+
// var (output, _) = ScriptTestRunner.Default.ExecuteFixture("CurrentContextualReflectionContext", "--isolated-load-context");
489+
// Assert.Contains("Dotnet.Script.Core.ScriptAssemblyLoadContext", output);
490+
}
491+
482492
private static string CreateTestScript(string scriptFolder)
483493
{
484494
string script = @"

src/Dotnet.Script.Tests/ScriptProjectProviderTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.IO;
2+
using System.Linq;
23
using System.Text;
4+
using System.Xml.Linq;
35
using Dotnet.Script.DependencyModel.Environment;
46
using Dotnet.Script.DependencyModel.ProjectSystem;
57
using Dotnet.Script.Shared.Tests;
@@ -30,5 +32,13 @@ public void ShouldLogProjectFileContent()
3032

3133
Assert.Contains("<Project Sdk=\"Microsoft.NET.Sdk\">", output);
3234
}
35+
36+
[Fact]
37+
public void ShouldUseSpecifiedSdk()
38+
{
39+
var provider = new ScriptProjectProvider(TestOutputHelper.CreateTestLogFactory());
40+
var projectFileInfo = provider.CreateProject(TestPathUtils.GetPathToTestFixtureFolder("WebApi"), _scriptEnvironment.TargetFramework, true);
41+
Assert.Equal("Microsoft.NET.Sdk.Web", XDocument.Load(projectFileInfo.Path).Descendants("Project").Single().Attributes("Sdk").Single().Value);
42+
}
3343
}
3444
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#r "sdk:Microsoft.NET.Sdk.Web"
2+
3+
using Microsoft.AspNetCore.Builder;
4+
5+
var a = WebApplication.Create();
6+
a.MapGet("/", () => "Hello world");

0 commit comments

Comments
 (0)