diff --git a/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets b/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
index 7e33684669..7d771e7c0d 100644
--- a/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
+++ b/src/Microsoft.NET.Sdk.Razor/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
@@ -69,11 +69,21 @@ Copyright (c) .NET Foundation. All rights reserved.
_RazorAddDebugSymbolsProjectOutputGroupOutput
+
+ $(PrepareForBuildDependsOn);
+ ResolveRazorGenerateInputs
+
+
_RazorPrepareForRun;
$(PrepareForRunDependsOn)
+
+ _RazorGetCopyToOutputDirectoryItems;
+ $(GetCopyToOutputDirectoryItems)
+
+
<_RazorDebugSymbolsIntermediatePath Condition="'$(_RazorDebugSymbolsProduced)'=='true'" Include="$(IntermediateOutputPath)$(RazorTargetName).pdb" />
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -384,7 +427,7 @@ Copyright (c) .NET Foundation. All rights reserved.
-->
+
+
+
diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
index 55791a7284..74bfdb1992 100644
--- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
+++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs
@@ -100,6 +100,30 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
throw new BuildOutputMissingException(result, match);
}
+ public static void BuildOutputDoesNotContainLine(MSBuildResult result, string match)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException(nameof(result));
+ }
+
+ if (match == null)
+ {
+ throw new ArgumentNullException(nameof(match));
+ }
+
+ // We don't really need to search line by line, I'm doing this so that it's possible/easy to debug.
+ var lines = result.Output.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+ for (var i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i].Trim();
+ if (line == match)
+ {
+ throw new BuildOutputContainsLineException(result, match);
+ }
+ }
+ }
+
public static void FileContains(MSBuildResult result, string filePath, string match)
{
if (result == null)
@@ -432,6 +456,19 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
protected override string Heading => $"Build did not contain the line: '{Match}'.";
}
+ private class BuildOutputContainsLineException : MSBuildXunitException
+ {
+ public BuildOutputContainsLineException(MSBuildResult result, string match)
+ : base(result)
+ {
+ Match = match;
+ }
+
+ public string Match { get; }
+
+ protected override string Heading => $"Build output contains the line: '{Match}'.";
+ }
+
private class FileContentFoundException : MSBuildXunitException
{
public FileContentFoundException(MSBuildResult result, string filePath, string content, string match)
diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIncrementalismTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIncrementalismTest.cs
index 288c46cf6a..f6b4c7ac4e 100644
--- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIncrementalismTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIncrementalismTest.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -136,5 +137,83 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileDoesNotExist(result, IntermediateOutputPath, "Razor", "Views", "Home", "Index.cshtml.g.cs");
}
}
+
+ [Fact]
+ [InitializeTestProject("AppWithP2PReference", additionalProjects: "ClassLibrary")]
+ public async Task IncrementalBuild_WithP2P_WorksWhenBuildProjectReferencesIsDisabled()
+ {
+ // Simulates building the same way VS does by setting BuildProjectReferences=false.
+ // With this flag, the only target called is GetCopyToOutputDirectoryItems on the referenced project.
+ // We need to ensure that we continue providing Razor binaries and symbols as files to be copied over.
+ var result = await DotnetMSBuild(target: default);
+
+ Assert.BuildPassed(result);
+
+ Assert.FileExists(result, OutputPath, "AppWithP2PReference.dll");
+ Assert.FileExists(result, OutputPath, "AppWithP2PReference.Views.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.Views.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.Views.pdb");
+
+ result = await DotnetMSBuild(target: "Clean", "/p:BuildProjectReferences=false", suppressRestore: true);
+ Assert.BuildPassed(result);
+
+ Assert.FileDoesNotExist(result, OutputPath, "AppWithP2PReference.dll");
+ Assert.FileDoesNotExist(result, OutputPath, "AppWithP2PReference.Views.dll");
+ Assert.FileDoesNotExist(result, OutputPath, "ClassLibrary.dll");
+ Assert.FileDoesNotExist(result, OutputPath, "ClassLibrary.Views.dll");
+ Assert.FileDoesNotExist(result, OutputPath, "ClassLibrary.Views.pdb");
+
+ // dotnet msbuild /p:BuildProjectReferences=false
+ result = await DotnetMSBuild(target: default, "/p:BuildProjectReferences=false", suppressRestore: true);
+
+ Assert.BuildPassed(result);
+ Assert.FileExists(result, OutputPath, "AppWithP2PReference.dll");
+ Assert.FileExists(result, OutputPath, "AppWithP2PReference.Views.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.Views.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.Views.pdb");
+ }
+
+ [Fact]
+ [InitializeTestProject("ClassLibrary")]
+ public async Task Build_TouchesUpToDateMarkerFile()
+ {
+ var classLibraryDll = Path.Combine(IntermediateOutputPath, "ClassLibrary.dll");
+ var classLibraryViewsDll = Path.Combine(IntermediateOutputPath, "ClassLibrary.Views.dll");
+ var markerFile = Path.Combine(IntermediateOutputPath, "ClassLibrary.csproj.CopyComplete");
+
+ var result = await DotnetMSBuild("Build");
+ Assert.BuildPassed(result);
+
+ Assert.FileExists(result, classLibraryDll);
+ Assert.FileExists(result, classLibraryViewsDll);
+ Assert.FileExists(result, markerFile);
+
+ // Gather thumbprints before incremental build.
+ var classLibraryThumbPrint = GetThumbPrint(classLibraryDll);
+ var classLibraryViewsThumbPrint = GetThumbPrint(classLibraryViewsDll);
+ var markerFileThumbPrint = GetThumbPrint(markerFile);
+
+ result = await DotnetMSBuild("Build");
+ Assert.BuildPassed(result);
+
+ // Verify thumbprint file is unchanged between true incremental builds
+ Assert.Equal(classLibraryThumbPrint, GetThumbPrint(classLibraryDll));
+ Assert.Equal(classLibraryViewsThumbPrint, GetThumbPrint(classLibraryViewsDll));
+ // In practice, this should remain unchanged. However, since our tests reference
+ // binaries from other projects, this file gets updated by Microsoft.Common.targets
+ Assert.NotEqual(markerFileThumbPrint, GetThumbPrint(markerFile));
+
+ // Change a cshtml file and verify ClassLibrary.Views.dll and marker file are updated
+ File.AppendAllText(Path.Combine(Project.DirectoryPath, "Views", "_ViewImports.cshtml"), Environment.NewLine);
+
+ result = await DotnetMSBuild("Build");
+ Assert.BuildPassed(result);
+
+ Assert.Equal(classLibraryThumbPrint, GetThumbPrint(classLibraryDll));
+ Assert.NotEqual(classLibraryViewsThumbPrint, GetThumbPrint(classLibraryViewsDll));
+ Assert.NotEqual(markerFileThumbPrint, GetThumbPrint(markerFile));
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntrospectionTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntrospectionTest.cs
index 1ddc59c75a..f19401a140 100644
--- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntrospectionTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntrospectionTest.cs
@@ -18,11 +18,12 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
[InitializeTestProject("SimpleMvc")]
public async Task RazorSdk_AddsCshtmlFilesToUpToDateCheckInput()
{
- var result = await DotnetMSBuild("_IntrospectUpToDateCheckInput");
+ var result = await DotnetMSBuild("_IntrospectUpToDateCheck");
Assert.BuildPassed(result);
Assert.BuildOutputContainsLine(result, $"UpToDateCheckInput: {Path.Combine("Views", "Home", "Index.cshtml")}");
Assert.BuildOutputContainsLine(result, $"UpToDateCheckInput: {Path.Combine("Views", "_ViewStart.cshtml")}");
+ Assert.BuildOutputContainsLine(result, $"UpToDateCheckBuilt: {Path.Combine(IntermediateOutputPath, "SimpleMvc.Views.dll")}");
}
[Fact]
@@ -45,5 +46,31 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.BuildPassed(result);
Assert.BuildOutputContainsLine(result, "UseRazorBuildServer: false");
}
+
+ [Fact]
+ [InitializeTestProject("ClassLibrary")]
+ public async Task GetCopyToOutputDirectoryItems_WhenNoFileIsPresent_ReturnsEmptySequence()
+ {
+ var result = await DotnetMSBuild(target: default);
+
+ Assert.BuildPassed(result);
+
+ Assert.FileExists(result, OutputPath, "ClassLibrary.dll");
+ Assert.FileExists(result, OutputPath, "ClassLibrary.Views.dll");
+
+ result = await DotnetMSBuild(target: "GetCopyToOutputDirectoryItems", "/t:_IntrospectGetCopyToOutputDirectoryItems /p:BuildProjectReferences=false", suppressRestore: true);
+ Assert.BuildPassed(result);
+ Assert.BuildOutputContainsLine(result, "AllItemsFullPathWithTargetPath: ClassLibrary.Views.dll");
+ Assert.BuildOutputContainsLine(result, "AllItemsFullPathWithTargetPath: ClassLibrary.Views.pdb");
+
+ // Remove all views from the class library
+ Directory.Delete(Path.Combine(Project.DirectoryPath, "Views"), recursive: true);
+
+ // dotnet msbuild /p:BuildProjectReferences=false
+ result = await DotnetMSBuild(target: "GetCopyToOutputDirectoryItems", "/t:_IntrospectGetCopyToOutputDirectoryItems /p:BuildProjectReferences=false", suppressRestore: true);
+
+ Assert.BuildOutputDoesNotContainLine(result, "AllItemsFullPathWithTargetPath: ClassLibrary.Views.dll");
+ Assert.BuildOutputDoesNotContainLine(result, "AllItemsFullPathWithTargetPath: ClassLibrary.Views.pdb");
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/PublishIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/PublishIntegrationTest.cs
index be6b1fed29..0931cfd43b 100644
--- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/PublishIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/PublishIntegrationTest.cs
@@ -27,6 +27,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileExists(result, PublishOutputPath, "SimpleMvc.Views.dll");
Assert.FileExists(result, PublishOutputPath, "SimpleMvc.Views.pdb");
+ // Verify assets get published
+ Assert.FileExists(result, PublishOutputPath, "wwwroot", "js", "SimpleMvc.js");
+ Assert.FileExists(result, PublishOutputPath, "wwwroot", "css", "site.css");
+
// By default refs and .cshtml files will not be copied on publish
Assert.FileCountEquals(result, 0, Path.Combine(PublishOutputPath, "refs"), "*.dll");
Assert.FileCountEquals(result, 0, Path.Combine(PublishOutputPath, "Views"), "*.cshtml");
@@ -294,6 +298,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileExists(result, PublishOutputPath, "ClassLibrary.pdb");
Assert.FileExists(result, PublishOutputPath, "ClassLibrary.Views.dll");
Assert.FileExists(result, PublishOutputPath, "ClassLibrary.Views.pdb");
+
+ // Verify fix for https://github.com/aspnet/Razor/issues/2295. No cshtml files should be published from the app
+ // or the ClassLibrary.
+ Assert.FileCountEquals(result, 0, Path.Combine(PublishOutputPath, "refs"), "*.dll");
+ Assert.FileCountEquals(result, 0, Path.Combine(PublishOutputPath, "Views"), "*.cshtml");
}
[Fact]
@@ -310,5 +319,40 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
Assert.FileDoesNotExist(result, OutputPath, "SimpleMvcFSharp.Views.dll");
Assert.FileDoesNotExist(result, OutputPath, "SimpleMvcFSharp.Views.pdb");
}
+
+ [Fact]
+ [InitializeTestProject("SimpleMvc")]
+ public async Task Publish_DoesNotPublishCustomRazorGenerateItems()
+ {
+ var additionalProjectContent = @"
+
+ false
+
+
+
+
+
+";
+ AddProjectFileContent(additionalProjectContent);
+ var result = await DotnetMSBuild("Publish");
+
+ Assert.BuildPassed(result);
+
+ Assert.FileExists(result, PublishOutputPath, "SimpleMvc.dll");
+ Assert.FileExists(result, PublishOutputPath, "SimpleMvc.pdb");
+ Assert.FileExists(result, PublishOutputPath, "SimpleMvc.Views.dll");
+ Assert.FileExists(result, PublishOutputPath, "SimpleMvc.Views.pdb");
+
+ // Verify assets get published
+ Assert.FileExists(result, PublishOutputPath, "wwwroot", "js", "SimpleMvc.js");
+ Assert.FileExists(result, PublishOutputPath, "wwwroot", "css", "site.css");
+
+ // By default refs and .cshtml files will not be copied on publish
+ Assert.FileCountEquals(result, 0, Path.Combine(PublishOutputPath, "refs"), "*.dll");
+ // Custom RazorGenerate item does not get published
+ Assert.FileDoesNotExist(result, PublishOutputPath, "Views", "Home", "Home.cshtml");
+ // cshtml Content item that's not part of RazorGenerate gets published.
+ Assert.FileExists(result, PublishOutputPath, "Views", "Home", "About.cshtml");
+ }
}
}
diff --git a/test/testapps/RazorTest.Introspection.targets b/test/testapps/RazorTest.Introspection.targets
index 575be8ef15..a22622b0d6 100644
--- a/test/testapps/RazorTest.Introspection.targets
+++ b/test/testapps/RazorTest.Introspection.targets
@@ -7,8 +7,11 @@
-
+
+
+
+
@@ -18,4 +21,8 @@
+
+
+
+
diff --git a/test/testapps/SimpleMvc/wwwroot/css/site.css b/test/testapps/SimpleMvc/wwwroot/css/site.css
new file mode 100644
index 0000000000..6b0e6c3ab2
--- /dev/null
+++ b/test/testapps/SimpleMvc/wwwroot/css/site.css
@@ -0,0 +1,37 @@
+/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification\
+for details on configuring this project to bundle and minify static web assets. */
+body {
+ padding-top: 50px;
+ padding-bottom: 20px;
+}
+
+/* Wrapping element */
+/* Set some basic padding to keep content from hitting the edges */
+.body-content {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+/* Carousel */
+.carousel-caption p {
+ font-size: 20px;
+ line-height: 1.4;
+}
+
+/* Make .svg files in the carousel display properly in older browsers */
+.carousel-inner .item img[src$=".svg"] {
+ width: 100%;
+}
+
+/* QR code generator */
+#qrCode {
+ margin: 15px;
+}
+
+/* Hide/rearrange for smaller screens */
+@media screen and (max-width: 767px) {
+ /* Hide captions */
+ .carousel-caption {
+ display: none;
+ }
+}
diff --git a/test/testapps/SimpleMvc/wwwroot/js/SimpleMvc.js b/test/testapps/SimpleMvc/wwwroot/js/SimpleMvc.js
new file mode 100644
index 0000000000..ad51101cd1
--- /dev/null
+++ b/test/testapps/SimpleMvc/wwwroot/js/SimpleMvc.js
@@ -0,0 +1 @@
+// This is a test file