diff --git a/Microsoft.DotNet.Cli.sln b/Microsoft.DotNet.Cli.sln index f7171285b..203948f0c 100644 --- a/Microsoft.DotNet.Cli.sln +++ b/Microsoft.DotNet.Cli.sln @@ -82,6 +82,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Installer", "Installer", "{ EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.DotNet.Cli.Msi.Tests", "test\Installer\Microsoft.DotNet.Cli.Msi.Tests\Microsoft.DotNet.Cli.Msi.Tests.xproj", "{0B31C336-149D-471A-B7B1-27B0F1E80F83}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.DependencyModel.Tests", "..\cli1\test\Microsoft.Extensions.DependencyModel.Tests\Microsoft.Extensions.DependencyModel.Tests.xproj", "{4A4711D8-4312-49FC-87B5-4F183F4C6A51}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -526,22 +528,22 @@ Global {BD4F0750-4E81-4AD2-90B5-E470881792C3}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU {BD4F0750-4E81-4AD2-90B5-E470881792C3}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU {BD4F0750-4E81-4AD2-90B5-E470881792C3}.RelWithDebInfo|x64.Build.0 = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Debug|x64.ActiveCfg = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Debug|x64.Build.0 = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Release|Any CPU.Build.0 = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Release|x64.ActiveCfg = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.Release|x64.Build.0 = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU - {0745410A-6629-47EB-AAB5-08D6288CAD72}.RelWithDebInfo|x64.Build.0 = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.ActiveCfg = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Debug|x64.Build.0 = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|Any CPU.Build.0 = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.ActiveCfg = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.Release|x64.Build.0 = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU + {4A4711D8-4312-49FC-87B5-4F183F4C6A51}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B31C336-149D-471A-B7B1-27B0F1E80F83}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -591,7 +593,7 @@ Global {947DD232-8D9B-4B78-9C6A-94F807D2DD58} = {713CBFBB-5392-438D-B766-A9A585EF1BB8} {947DD232-8D9B-4B78-9C6A-94F807D22222} = {713CBFBB-5392-438D-B766-A9A585EF1BB8} {BD4F0750-4E81-4AD2-90B5-E470881792C3} = {ED2FE3E2-F7E7-4389-8231-B65123F2076F} - {0745410A-6629-47EB-AAB5-08D6288CAD72} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} + {4A4711D8-4312-49FC-87B5-4F183F4C6A51} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} {0E3300A4-DF54-40BF-87D8-E7658330C288} = {17735A9D-BFD9-4585-A7CB-3208CA6EA8A7} {0B31C336-149D-471A-B7B1-27B0F1E80F83} = {0E3300A4-DF54-40BF-87D8-E7658330C288} EndGlobalSection diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/NuGet.Config b/TestAssets/TestProjects/TestAppWithContentPackage/NuGet.Config new file mode 100644 index 000000000..4a43c6b88 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithContentPackage/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/Packages/SharedContentA.1.0.0.nupkg b/TestAssets/TestProjects/TestAppWithContentPackage/Packages/SharedContentA.1.0.0.nupkg new file mode 100644 index 000000000..22c50bfed Binary files /dev/null and b/TestAssets/TestProjects/TestAppWithContentPackage/Packages/SharedContentA.1.0.0.nupkg differ diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/Program.cs b/TestAssets/TestProjects/TestAppWithContentPackage/Program.cs new file mode 100644 index 000000000..b71b81142 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithContentPackage/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Reflection; + +namespace TestAppWithContentPackage +{ + public class Program + { + public static int Main(string[] args) + { + foreach (var name in Assembly.GetEntryAssembly().GetManifestResourceNames()) + { + Console.WriteLine(name); + } + Console.WriteLine(typeof(Foo).FullName); + Console.WriteLine(typeof(MyNamespace.Util).FullName); + return 0; + } + } +} diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj b/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj new file mode 100644 index 000000000..718199108 --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithContentPackage/TestAppWithContentPackage.xproj @@ -0,0 +1,19 @@ + + + + 14.0.24720 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + ea239e10-75e8-4305-966e-fec926a5aee6 + TestAppWithContentPackage + ..\..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + \ No newline at end of file diff --git a/TestAssets/TestProjects/TestAppWithContentPackage/project.json b/TestAssets/TestProjects/TestAppWithContentPackage/project.json new file mode 100644 index 000000000..d9dd13dfc --- /dev/null +++ b/TestAssets/TestProjects/TestAppWithContentPackage/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + + "dependencies": { + "NETStandard.Library": "1.0.0-rc2-23811", + "SharedContentA": "1.0.0-*" + }, + "frameworks": { + "dnxcore50": { } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.Compiler.Common/Executable.cs b/src/Microsoft.DotNet.Compiler.Common/Executable.cs index 7cbc53ec4..31f92825f 100644 --- a/src/Microsoft.DotNet.Compiler.Common/Executable.cs +++ b/src/Microsoft.DotNet.Compiler.Common/Executable.cs @@ -20,14 +20,20 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common { private readonly ProjectContext _context; + private readonly LibraryExporter _exporter; + private readonly OutputPaths _outputPaths; - private readonly LibraryExporter _exporter; + private readonly string _runtimeOutputPath; + + private readonly string _intermediateOutputPath; public Executable(ProjectContext context, OutputPaths outputPaths, LibraryExporter exporter) { _context = context; _outputPaths = outputPaths; + _runtimeOutputPath = outputPaths.RuntimeOutputPath; + _intermediateOutputPath = outputPaths.IntermediateOutputDirectoryPath; _exporter = exporter; } @@ -38,68 +44,75 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common throw new InvalidOperationException($"Can not make output runnable for framework {_context.TargetFramework}, because it doesn't have runtime target"); } - var outputPath = _outputPaths.RuntimeOutputPath; - - CopyContentFiles(outputPath); - - ExportRuntimeAssets(outputPath); + CopyContentFiles(); + ExportRuntimeAssets(); } - private void ExportRuntimeAssets(string outputPath) + private void ExportRuntimeAssets() { if (_context.TargetFramework.IsDesktop()) { - MakeCompilationOutputRunnableForFullFramework(outputPath); + MakeCompilationOutputRunnableForFullFramework(); } else { - MakeCompilationOutputRunnableForCoreCLR(outputPath); + MakeCompilationOutputRunnableForCoreCLR(); } } - private void MakeCompilationOutputRunnableForFullFramework( - string outputPath) + private void MakeCompilationOutputRunnableForFullFramework() { - CopyAllDependencies(outputPath, _exporter.GetDependencies()); + var dependencies = _exporter.GetDependencies(); + CopyAssemblies(dependencies); + CopyAssets(dependencies); GenerateBindingRedirects(_exporter); } - private void MakeCompilationOutputRunnableForCoreCLR(string outputPath) + private void MakeCompilationOutputRunnableForCoreCLR() { - WriteDepsFileAndCopyProjectDependencies(_exporter, _context.ProjectFile.Name, outputPath); + WriteDepsFileAndCopyProjectDependencies(_exporter); // TODO: Pick a host based on the RID - CoreHost.CopyTo(outputPath, _context.ProjectFile.Name + Constants.ExeSuffix); + CoreHost.CopyTo(_runtimeOutputPath, _context.ProjectFile.Name + Constants.ExeSuffix); } - private void CopyContentFiles(string outputPath) + private void CopyContentFiles() { var contentFiles = new ContentFiles(_context); - contentFiles.StructuredCopyTo(outputPath); + contentFiles.StructuredCopyTo(_runtimeOutputPath); } - private static void CopyAllDependencies(string outputPath, IEnumerable libraryExports) + private void CopyAssemblies(IEnumerable libraryExports) { foreach (var libraryExport in libraryExports) { - libraryExport.RuntimeAssemblies.CopyTo(outputPath); - libraryExport.NativeLibraries.CopyTo(outputPath); - libraryExport.RuntimeAssets.StructuredCopyTo(outputPath); + libraryExport.RuntimeAssemblies.CopyTo(_runtimeOutputPath); + libraryExport.NativeLibraries.CopyTo(_runtimeOutputPath); } } - private static void WriteDepsFileAndCopyProjectDependencies( - LibraryExporter exporter, - string projectFileName, - string outputPath) + private void CopyAssets(IEnumerable libraryExports) + { + foreach (var libraryExport in libraryExports) + { + libraryExport.RuntimeAssets.StructuredCopyTo( + _runtimeOutputPath, + _intermediateOutputPath); + } + } + + private void WriteDepsFileAndCopyProjectDependencies(LibraryExporter exporter) { exporter .GetDependencies(LibraryType.Package) - .WriteDepsTo(Path.Combine(outputPath, projectFileName + FileNameSuffixes.Deps)); + .WriteDepsTo(Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.Deps)); var projectExports = exporter.GetDependencies(LibraryType.Project); + CopyAssemblies(projectExports); + CopyAssets(projectExports); - CopyAllDependencies(outputPath, projectExports); + var packageExports = exporter.GetDependencies(LibraryType.Package); + CopyAssets(packageExports); } public void GenerateBindingRedirects(LibraryExporter exporter) diff --git a/src/Microsoft.DotNet.Compiler.Common/LibraryExporterExtensions.cs b/src/Microsoft.DotNet.Compiler.Common/LibraryExporterExtensions.cs index bf7e7403f..c4e088401 100644 --- a/src/Microsoft.DotNet.Compiler.Common/LibraryExporterExtensions.cs +++ b/src/Microsoft.DotNet.Compiler.Common/LibraryExporterExtensions.cs @@ -53,7 +53,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common } } - public static void StructuredCopyTo(this IEnumerable assets, string destinationPath) + public static void StructuredCopyTo(this IEnumerable assets, string destinationPath, string tempLocation) { if (!Directory.Exists(destinationPath)) { @@ -63,8 +63,9 @@ namespace Microsoft.DotNet.Cli.Compiler.Common foreach (var asset in assets) { var targetName = ResolveTargetName(destinationPath, asset); + var transformedFile = asset.GetTransformedFile(tempLocation); - File.Copy(asset.ResolvedPath, targetName, overwrite: true); + File.Copy(transformedFile, targetName, overwrite: true); } } diff --git a/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj b/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj new file mode 100644 index 000000000..b898305b0 --- /dev/null +++ b/src/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.xproj @@ -0,0 +1,18 @@ + + + + 14.0.24720 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + bd4f0750-4e81-4ad2-90b5-e470881792c3 + Microsoft.DotNet.InternalAbstractions + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel.Workspaces/ProjectJsonWorkspace.cs b/src/Microsoft.DotNet.ProjectModel.Workspaces/ProjectJsonWorkspace.cs index 8560e426e..9dbdedf8a 100644 --- a/src/Microsoft.DotNet.ProjectModel.Workspaces/ProjectJsonWorkspace.cs +++ b/src/Microsoft.DotNet.ProjectModel.Workspaces/ProjectJsonWorkspace.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; using Microsoft.DotNet.Cli.Compiler.Common; +using Microsoft.DotNet.ProjectModel.Compilation; using NuGet.Frameworks; namespace Microsoft.DotNet.ProjectModel.Workspaces @@ -78,7 +79,10 @@ namespace Microsoft.DotNet.ProjectModel.Workspaces foreach (var file in project.ProjectFile.Files.SourceFiles) { - AddSourceFile(projectInfo, file); + using (var stream = File.OpenRead(file)) + { + AddSourceFile(projectInfo, file, stream); + } } var exporter = project.CreateExporter(configuration); @@ -104,24 +108,24 @@ namespace Microsoft.DotNet.ProjectModel.Workspaces foreach (var file in dependency.SourceReferences) { - AddSourceFile(projectInfo, file); + using (var stream = file.GetTransformedStream()) + { + AddSourceFile(projectInfo, file.ResolvedPath, stream); + } } } return projectInfo.Id; } - private void AddSourceFile(ProjectInfo projectInfo, string file) + private void AddSourceFile(ProjectInfo projectInfo, string file, Stream stream) { - using (var stream = File.OpenRead(file)) - { - var sourceText = SourceText.From(stream, encoding: Encoding.UTF8); - var id = DocumentId.CreateNewId(projectInfo.Id); - var version = VersionStamp.Create(); + var sourceText = SourceText.From(stream, encoding: Encoding.UTF8); + var id = DocumentId.CreateNewId(projectInfo.Id); + var version = VersionStamp.Create(); - var loader = TextLoader.From(TextAndVersion.Create(sourceText, version)); - OnDocumentAdded(DocumentInfo.Create(id, file, filePath: file, loader: loader)); - } + var loader = TextLoader.From(TextAndVersion.Create(sourceText, version)); + OnDocumentAdded(DocumentInfo.Create(id, file, filePath: file, loader: loader)); } private MetadataReference GetMetadataReference(string path) diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAsset.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAsset.cs index d7740a94d..6186f26e5 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAsset.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAsset.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using Microsoft.DotNet.ProjectModel.Utilities; using Microsoft.Extensions.Internal; namespace Microsoft.DotNet.ProjectModel.Compilation @@ -13,12 +15,14 @@ namespace Microsoft.DotNet.ProjectModel.Compilation public string Name { get; } public string RelativePath { get; } public string ResolvedPath { get; } + public Action Transform { get; set; } - public LibraryAsset(string name, string relativePath, string resolvedPath) + public LibraryAsset(string name, string relativePath, string resolvedPath, Action transform = null) { Name = name; RelativePath = relativePath; ResolvedPath = resolvedPath; + Transform = transform; } public bool Equals(LibraryAsset other) @@ -42,5 +46,25 @@ namespace Microsoft.DotNet.ProjectModel.Compilation combiner.Add(ResolvedPath); return combiner.CombinedHash; } + + public static LibraryAsset CreateFromRelativePath(string basePath, string relativePath, Action transform = null) + { + return new LibraryAsset( + Path.GetFileNameWithoutExtension(relativePath), + relativePath, + Path.Combine(basePath, relativePath), + transform); + } + + public static LibraryAsset CreateFromAbsolutePath(string basePath, string absolutePath, Action transform = null) + { + var relativePath = absolutePath.Replace(PathUtility.EnsureTrailingSlash(basePath), string.Empty); + + return new LibraryAsset( + Path.GetFileNameWithoutExtension(relativePath), + relativePath, + absolutePath, + transform); + } } } diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAssetExtension.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAssetExtension.cs new file mode 100644 index 000000000..93b466639 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryAssetExtension.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.IO; + +namespace Microsoft.DotNet.ProjectModel.Compilation +{ + public static class LibraryAssetExtensions + { + public static string GetTransformedFile(this LibraryAsset asset, string tempLocation, string tempName = null) + { + if (asset.Transform == null) + { + return asset.ResolvedPath; + } + + tempName = tempName ?? Path.GetFileName(asset.RelativePath); + using (var input = File.OpenRead(asset.ResolvedPath)) + { + var transformedName = Path.Combine(tempLocation, tempName); + using (var output = File.OpenWrite(transformedName)) + { + asset.Transform(input, output); + } + return transformedName; + } + } + + public static Stream GetTransformedStream(this LibraryAsset asset) + { + if (asset.Transform == null) + { + return File.OpenRead(asset.ResolvedPath); + } + + using (var input = File.OpenRead(asset.ResolvedPath)) + { + var output = new MemoryStream(); + asset.Transform(input, output); + return output; + } + } + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExport.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExport.cs index 1db48a791..0380fc56a 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExport.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExport.cs @@ -34,10 +34,15 @@ namespace Microsoft.DotNet.ProjectModel.Compilation /// public IEnumerable CompilationAssemblies { get; } + /// + /// Get a list of embedded resource files provided by this export. + /// + public IEnumerable EmbeddedResources { get; } + /// /// Gets a list of fully-qualified paths to source code file references /// - public IEnumerable SourceReferences { get; } + public IEnumerable SourceReferences { get; } /// /// Get a list of analyzers provided by this export. @@ -46,10 +51,11 @@ namespace Microsoft.DotNet.ProjectModel.Compilation public LibraryExport(LibraryDescription library, IEnumerable compileAssemblies, - IEnumerable sourceReferences, + IEnumerable sourceReferences, IEnumerable runtimeAssemblies, IEnumerable runtimeAssets, IEnumerable nativeLibraries, + IEnumerable embeddedResources, IEnumerable analyzers) { Library = library; @@ -58,6 +64,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation RuntimeAssemblies = runtimeAssemblies; RuntimeAssets = runtimeAssets; NativeLibraries = nativeLibraries; + EmbeddedResources = embeddedResources; AnalyzerReferences = analyzers; } diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExportBuilder.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExportBuilder.cs new file mode 100644 index 000000000..42d98c897 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExportBuilder.cs @@ -0,0 +1,173 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.ProjectModel.Compilation +{ + public class LibraryExportBuilder + { + private IList _runtimeAssemblies; + + private IList _runtimeAssets; + + private IList _compilationAssemblies; + + private IList _compilationAssets; + + private IList _debugAssets; + + private IList _sourceReferences; + + private IList _nativeLibraries; + + private IList _embeddedResources; + + private IList _analyzerReferences; + + public LibraryDescription Library { get; set; } + + public IEnumerable RuntimeAssemblies => _runtimeAssemblies; + + public IEnumerable RuntimeAssets => _runtimeAssets; + + public IEnumerable CompilationAssemblies => _compilationAssemblies; + + public IEnumerable CompilationAssets => _compilationAssets; + + public IEnumerable SourceReferences => _sourceReferences; + + public IEnumerable NativeLibraries => _nativeLibraries; + + public IEnumerable EmbeddedResources => _embeddedResources; + + public IEnumerable AnalyzerReferences => _analyzerReferences; + + public static LibraryExportBuilder Create(LibraryDescription library = null) + { + return new LibraryExportBuilder().WithLibrary(library); + } + + public LibraryExport Build() + { + if (Library == null) + { + throw new InvalidOperationException("Cannot build LibraryExport withoud Library set"); + } + return new LibraryExport( + Library, + CompilationAssemblies ?? EmptyArray.Value, + SourceReferences ?? EmptyArray.Value, + RuntimeAssemblies ?? EmptyArray.Value, + RuntimeAssets ?? EmptyArray.Value, + NativeLibraries ?? EmptyArray.Value, + EmbeddedResources ?? EmptyArray.Value, + AnalyzerReferences ?? EmptyArray.Value); + } + + public LibraryExportBuilder WithLibrary(LibraryDescription libraryDescription) + { + Library = libraryDescription; + return this; + } + + public LibraryExportBuilder WithRuntimeAssemblies(IEnumerable assets) + { + Replace(ref _runtimeAssemblies, assets); + return this; + } + + public LibraryExportBuilder WithRuntimeAssets(IEnumerable assets) + { + Replace(ref _runtimeAssets, assets); + return this; + } + + public LibraryExportBuilder WithCompilationAssemblies(IEnumerable assets) + { + Replace(ref _compilationAssemblies, assets); + return this; + } + + public LibraryExportBuilder WithSourceReferences(IEnumerable assets) + { + Replace(ref _sourceReferences, assets); + return this; + } + + public LibraryExportBuilder WithNativeLibraries(IEnumerable assets) + { + Replace(ref _nativeLibraries, assets); + return this; + } + + public LibraryExportBuilder WithEmbedddedResources(IEnumerable assets) + { + Replace(ref _embeddedResources, assets); + return this; + } + + public LibraryExportBuilder WithAnalyzerReference(IEnumerable assets) + { + Replace(ref _analyzerReferences, assets); + return this; + } + + public LibraryExportBuilder AddRuntimeAssembly(LibraryAsset asset) + { + Add(ref _runtimeAssemblies, asset); + return this; + } + + public LibraryExportBuilder AddRuntimeAsset(LibraryAsset asset) + { + Add(ref _runtimeAssets, asset); + return this; + } + + public LibraryExportBuilder AddCompilationAssembly(LibraryAsset asset) + { + Add(ref _compilationAssemblies, asset); + return this; + } + + public LibraryExportBuilder AddSourceReference(LibraryAsset asset) + { + Add(ref _sourceReferences, asset); + return this; + } + + public LibraryExportBuilder AddNativeLibrary(LibraryAsset asset) + { + Add(ref _compilationAssets, asset); + return this; + } + + public LibraryExportBuilder AddEmbedddedResource(LibraryAsset asset) + { + Add(ref _embeddedResources, asset); + return this; + } + + public LibraryExportBuilder AddAnalyzerReference(AnalyzerReference asset) + { + Add(ref _analyzerReferences, asset); + return this; + } + + private void Replace(ref IList list, IEnumerable enumerable) + { + list = new List(enumerable); + } + + private void Add(ref IList list, T item) + { + if (list == null) + { + list = new List(); + } + list.Add(item); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs index 706d16ab0..ea5989e83 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs @@ -6,9 +6,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using Microsoft.DotNet.ProjectModel.Compilation.Preprocessor; +using Microsoft.DotNet.ProjectModel.Files; using Microsoft.DotNet.ProjectModel.Graph; using Microsoft.DotNet.ProjectModel.Resolution; using Microsoft.DotNet.ProjectModel.Utilities; +using Microsoft.DotNet.Tools.Compiler; using NuGet.Frameworks; namespace Microsoft.DotNet.ProjectModel.Compilation @@ -89,7 +92,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation } var compilationAssemblies = new List(); - var sourceReferences = new List(); + var sourceReferences = new List(); var analyzerReferences = new List(); var libraryExport = GetExport(library); @@ -111,13 +114,15 @@ namespace Microsoft.DotNet.ProjectModel.Compilation analyzerReferences.AddRange(libraryExport.AnalyzerReferences); } - yield return new LibraryExport(library, - compilationAssemblies, - sourceReferences, - libraryExport.RuntimeAssemblies, - libraryExport.RuntimeAssets, - libraryExport.NativeLibraries, - analyzerReferences); + yield return LibraryExportBuilder.Create(library) + .WithCompilationAssemblies(compilationAssemblies) + .WithSourceReferences(sourceReferences) + .WithRuntimeAssemblies(libraryExport.RuntimeAssemblies) + .WithRuntimeAssets(libraryExport.RuntimeAssets) + .WithNativeLibraries(libraryExport.NativeLibraries) + .WithEmbedddedResources(libraryExport.EmbeddedResources) + .WithAnalyzerReference(analyzerReferences) + .Build(); } } @@ -131,13 +136,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation if (!library.Resolved) { // For a unresolved project reference returns a export with empty asset. - return new LibraryExport(library: library, - compileAssemblies: Enumerable.Empty(), - sourceReferences: Enumerable.Empty(), - nativeLibraries: Enumerable.Empty(), - runtimeAssets: Enumerable.Empty(), - runtimeAssemblies: EmptyArray.Value, - analyzers: EmptyArray.Value); + return LibraryExportBuilder.Create(library).Build(); } if (Equals(LibraryType.Package, library.Identity.Type)) @@ -156,32 +155,57 @@ namespace Microsoft.DotNet.ProjectModel.Compilation private LibraryExport ExportPackage(PackageDescription package) { - var nativeLibraries = new List(); - PopulateAssets(package, package.NativeLibraries, nativeLibraries); + var builder = LibraryExportBuilder.Create(package); + builder.WithNativeLibraries(PopulateAssets(package, package.NativeLibraries)); + builder.WithRuntimeAssemblies(PopulateAssets(package, package.RuntimeAssemblies)); + builder.WithCompilationAssemblies(PopulateAssets(package, package.CompileTimeAssemblies)); + builder.WithSourceReferences(GetSharedSources(package)); + builder.WithAnalyzerReference(GetAnalyzerReferences(package)); - var runtimeAssemblies = new List(); - PopulateAssets(package, package.RuntimeAssemblies, runtimeAssemblies); - - var compileAssemblies = new List(); - PopulateAssets(package, package.CompileTimeAssemblies, compileAssemblies); - - var sourceReferences = new List(); - foreach (var sharedSource in GetSharedSources(package)) + if (package.ContentFiles.Any()) { - sourceReferences.Add(sharedSource); + var parameters = PPFileParameters.CreateForProject(_rootProject.Project); + Action transform = (input, output) => PPFilePreprocessor.Preprocess(input, output, parameters); + + var sourceCodeLanguage = _rootProject.Project.GetSourceCodeLanguage(); + var languageGroups = package.ContentFiles.GroupBy(file => file.CodeLanguage); + var selectedGroup = languageGroups.FirstOrDefault(g => g.Key == sourceCodeLanguage) ?? + languageGroups.FirstOrDefault(g => g.Key == null); + if (selectedGroup != null) + { + foreach (var contentFile in selectedGroup) + { + if (contentFile.CodeLanguage != null && + string.Compare(contentFile.CodeLanguage, sourceCodeLanguage, StringComparison.OrdinalIgnoreCase) != 0) + { + continue; + } + + var fileTransform = contentFile.PPOutputPath != null ? transform : null; + + var fullPath = Path.Combine(package.Path, contentFile.Path); + if (contentFile.BuildAction == BuildAction.Compile) + { + builder.AddSourceReference(LibraryAsset.CreateFromRelativePath(package.Path, contentFile.Path, fileTransform)); + } + else if (contentFile.BuildAction == BuildAction.EmbeddedResource) + { + builder.AddEmbedddedResource(LibraryAsset.CreateFromRelativePath(package.Path, contentFile.Path, fileTransform)); + } + if (contentFile.CopyToOutput) + { + builder.AddRuntimeAsset(new LibraryAsset(contentFile.Path, contentFile.OutputPath, fullPath, fileTransform)); + } + } + } } - var analyzers = GetAnalyzerReferences(package); - - return new LibraryExport(package, compileAssemblies, - sourceReferences, runtimeAssemblies, EmptyArray.Value, nativeLibraries, analyzers); + return builder.Build(); } private LibraryExport ExportProject(ProjectDescription project) { - var compileAssemblies = new List(); - var runtimeAssets = new List(); - var sourceReferences = new List(); + var builder = LibraryExportBuilder.Create(project); if (!string.IsNullOrEmpty(project.TargetFrameworkInfo?.AssemblyPath)) { @@ -194,50 +218,61 @@ namespace Microsoft.DotNet.ProjectModel.Compilation null, Path.GetFullPath(Path.Combine(project.Project.ProjectDirectory, assemblyPath))); - compileAssemblies.Add(compileAsset); - runtimeAssets.Add(new LibraryAsset(Path.GetFileName(pdbPath), Path.GetFileName(pdbPath), pdbPath)); + builder.AddCompilationAssembly(compileAsset); + builder.AddRuntimeAsset(new LibraryAsset(Path.GetFileName(pdbPath), Path.GetFileName(pdbPath), pdbPath)); } else if (project.Project.Files.SourceFiles.Any()) { var outputPaths = project.GetOutputPaths(_buildBasePath, _solutionRootPath, _configuration, _runtime); - CompilationOutputFiles files; - if (project == _rootProject && - !string.IsNullOrWhiteSpace(_runtime) && - project.Project.HasRuntimeOutput(_configuration)) + var compilationAssembly = outputPaths.CompilationFiles.Assembly; + var compilationAssemblyAsset = LibraryAsset.CreateFromAbsolutePath( + outputPaths.CompilationFiles.BasePath, + compilationAssembly); + + builder.AddCompilationAssembly(compilationAssemblyAsset); + + if (ExportsRuntime(project)) { - files = outputPaths.RuntimeFiles; + var runtimeAssemblyAsset = LibraryAsset.CreateFromAbsolutePath( + outputPaths.RuntimeFiles.BasePath, + outputPaths.RuntimeFiles.Assembly); + + builder.AddRuntimeAssembly(runtimeAssemblyAsset); + builder.WithRuntimeAssets(CollectAssets(outputPaths.RuntimeFiles)); } else { - files = outputPaths.CompilationFiles; - } - - var assemblyPath = files.Assembly; - compileAssemblies.Add(new LibraryAsset(project.Identity.Name, null, assemblyPath)); - - foreach (var path in files.All()) - { - if (string.Equals(assemblyPath, path)) - { - continue; - } - - runtimeAssets.Add(new LibraryAsset(Path.GetFileName(path), path.Replace(files.BasePath, string.Empty), path)); + builder.AddRuntimeAssembly(compilationAssemblyAsset); + builder.WithRuntimeAssets(CollectAssets(outputPaths.CompilationFiles)); } } - // Add shared sources - foreach (var sharedFile in project.Project.Files.SharedFiles) + builder.WithSourceReferences(project.Project.Files.SharedFiles.Select(f => + LibraryAsset.CreateFromAbsolutePath(project.Path, f) + )); + + return builder.Build(); + } + + private IEnumerable CollectAssets(CompilationOutputFiles files) + { + var assemblyPath = files.Assembly; + foreach (var path in files.All()) { - sourceReferences.Add(sharedFile); + if (string.Equals(assemblyPath, path)) + { + continue; + } + yield return LibraryAsset.CreateFromAbsolutePath(files.BasePath, path); } + } - // No support for ref or native in projects, so runtimeAssemblies is - // just the same as compileAssemblies and nativeLibraries are empty - // Also no support for analyzer projects - return new LibraryExport(project, compileAssemblies, sourceReferences, - compileAssemblies, runtimeAssets, EmptyArray.Value, EmptyArray.Value); + private bool ExportsRuntime(ProjectDescription project) + { + return project == _rootProject && + !string.IsNullOrWhiteSpace(_runtime) && + project.Project.HasRuntimeOutput(_configuration); } private static string ResolvePath(Project project, string configuration, string path) @@ -258,25 +293,24 @@ namespace Microsoft.DotNet.ProjectModel.Compilation { // We assume the path is to an assembly. Framework libraries only export compile-time stuff // since they assume the runtime library is present already - return new LibraryExport( - library, - string.IsNullOrEmpty(library.Path) ? - EmptyArray.Value : - new[] { new LibraryAsset(library.Identity.Name, library.Path, library.Path) }, - EmptyArray.Value, - EmptyArray.Value, - EmptyArray.Value, - EmptyArray.Value, - EmptyArray.Value); + var builder = LibraryExportBuilder.Create(library); + if (!string.IsNullOrEmpty(library.Path)) + { + builder.WithCompilationAssemblies(new [] + { + new LibraryAsset(library.Identity.Name, null, library.Path) + }); + } + return builder.Build(); } - private IEnumerable GetSharedSources(PackageDescription package) + private IEnumerable GetSharedSources(PackageDescription package) { return package .Library .Files .Where(path => path.StartsWith("shared" + Path.DirectorySeparatorChar)) - .Select(path => Path.Combine(package.Path, path)); + .Select(path => LibraryAsset.CreateFromRelativePath(package.Path, path)); } private IEnumerable GetAnalyzerReferences(PackageDescription package) @@ -348,14 +382,11 @@ namespace Microsoft.DotNet.ProjectModel.Compilation } - private void PopulateAssets(PackageDescription package, IEnumerable section, IList assets) + private IEnumerable PopulateAssets(PackageDescription package, IEnumerable section) { foreach (var assemblyPath in section) { - assets.Add(new LibraryAsset( - Path.GetFileNameWithoutExtension(assemblyPath), - assemblyPath, - Path.Combine(package.Path, assemblyPath))); + yield return LibraryAsset.CreateFromRelativePath(package.Path, assemblyPath.Path); } } diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileParameters.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileParameters.cs new file mode 100644 index 000000000..eca04ad66 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileParameters.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.ProjectModel.Compilation.Preprocessor +{ + public class PPFileParameters + { + public static IDictionary CreateForProject(Project project) + { + return new Dictionary() + { + {"rootnamespace", project.Name }, + {"assemblyname", project.Name } + }; + } + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFilePreprocessor.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFilePreprocessor.cs new file mode 100644 index 000000000..c2d597c97 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFilePreprocessor.cs @@ -0,0 +1,53 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.DotNet.Tools.Compiler +{ + public class PPFilePreprocessor + { + public static void Preprocess(Stream input, Stream output, IDictionary parameters) + { + string text; + using (var streamReader = new StreamReader(input)) + { + text = streamReader.ReadToEnd(); + } + var tokenizer = new PPFileTokenizer(text); + using (var streamWriter = new StreamWriter(output)) + { + while (true) + { + var token = tokenizer.Read(); + if (token == null) + { + break; + } + + if (token.Category == PPFileTokenizer.TokenCategory.Variable) + { + var replaced = ReplaceToken(token.Value, parameters); + streamWriter.Write(replaced); + } + else + { + streamWriter.Write(token.Value); + } + } + } + } + + private static string ReplaceToken(string name, IDictionary parameters) + { + string value; + if (!parameters.TryGetValue(name, out value)) + { + throw new InvalidOperationException($"The replacement token '{name}' has no value."); + } + return value; + } + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileTokenizer.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileTokenizer.cs new file mode 100644 index 000000000..8567aeb48 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/Preprocessor/PPFileTokenizer.cs @@ -0,0 +1,124 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; +using System.Text; + +namespace Microsoft.DotNet.Tools.Compiler +{ + public class PPFileTokenizer + { + private readonly string _text; + private int _index; + + public PPFileTokenizer(string text) + { + _text = text; + _index = 0; + } + + /// + /// Gets the next token. + /// + /// The parsed token. Or null if no more tokens are available. + public Token Read() + { + if (_index >= _text.Length) + { + return null; + } + + if (_text[_index] == '$') + { + _index++; + return ParseTokenAfterDollarSign(); + } + return ParseText(); + } + + private static bool IsWordChar(char ch) + { + // See http://msdn.microsoft.com/en-us/library/20bw873z.aspx#WordCharacter + var c = CharUnicodeInfo.GetUnicodeCategory(ch); + return c == UnicodeCategory.LowercaseLetter || + c == UnicodeCategory.UppercaseLetter || + c == UnicodeCategory.TitlecaseLetter || + c == UnicodeCategory.OtherLetter || + c == UnicodeCategory.ModifierLetter || + c == UnicodeCategory.DecimalDigitNumber || + c == UnicodeCategory.ConnectorPunctuation; + } + + // Parses and returns the next token after a $ is just read. + // _index is one char after the $. + private Token ParseTokenAfterDollarSign() + { + var sb = new StringBuilder(); + while (_index < _text.Length) + { + var ch = _text[_index]; + if (ch == '$') + { + ++_index; + if (sb.Length == 0) + { + // escape sequence "$$" is encountered + return new Token(TokenCategory.Text, "$"); + } + // matching $ is read. So the token is a variable. + return new Token(TokenCategory.Variable, sb.ToString()); + } + if (IsWordChar(ch)) + { + sb.Append(ch); + ++_index; + } + else + { + // non word char encountered. So the current token + // is not a variable after all. + sb.Insert(0, '$'); + sb.Append(ch); + ++_index; + return new Token(TokenCategory.Text, sb.ToString()); + } + } + + // no matching $ is found and the end of text is reached. + // So the current token is a text. + sb.Insert(0, '$'); + return new Token(TokenCategory.Text, sb.ToString()); + } + + private Token ParseText() + { + var sb = new StringBuilder(); + while (_index < _text.Length + && _text[_index] != '$') + { + sb.Append(_text[_index]); + _index++; + } + + return new Token(TokenCategory.Text, sb.ToString()); + } + + public class Token + { + public string Value { get; private set; } + public TokenCategory Category { get; private set; } + + public Token(TokenCategory category, string value) + { + Category = category; + Value = value; + } + } + + public enum TokenCategory + { + Text, + Variable + } + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/BuildAction.cs b/src/Microsoft.DotNet.ProjectModel/Graph/BuildAction.cs new file mode 100644 index 000000000..8d59abbb0 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Graph/BuildAction.cs @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.ProjectModel.Graph +{ + public struct BuildAction : IEquatable + { + public static readonly BuildAction Compile = new BuildAction(nameof(Compile)); + public static readonly BuildAction EmbeddedResource = new BuildAction(nameof(EmbeddedResource)); + public static readonly BuildAction Resource = new BuildAction(nameof(Resource)); + + // Default value + public static readonly BuildAction None = new BuildAction(nameof(None)); + + public string Value { get; } + + private BuildAction(string value) + { + Value = value; + } + + public static bool TryParse(string value, out BuildAction type) + { + // We only support values we know about + if (string.Equals(Compile.Value, value, StringComparison.OrdinalIgnoreCase)) + { + type = Compile; + return true; + } + else if (string.Equals(EmbeddedResource.Value, value, StringComparison.OrdinalIgnoreCase)) + { + type = EmbeddedResource; + return true; + } + else if (string.Equals(Resource.Value, value, StringComparison.OrdinalIgnoreCase)) + { + type = Resource; + return true; + } + else if (string.Equals(None.Value, value, StringComparison.OrdinalIgnoreCase)) + { + type = None; + return true; + } + type = None; + return false; + } + + public override string ToString() + { + return $"BuildAction.{Value}"; + } + + public bool Equals(BuildAction other) + { + return string.Equals(other.Value, Value, StringComparison.OrdinalIgnoreCase); + } + + public override bool Equals(object obj) + { + return obj is BuildAction && Equals((BuildAction)obj); + } + + public static bool operator ==(BuildAction left, BuildAction right) + { + return Equals(left, right); + } + + public static bool operator !=(BuildAction left, BuildAction right) + { + return !Equals(left, right); + } + + public override int GetHashCode() + { + if (string.IsNullOrEmpty(Value)) + { + return 0; + } + return StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs index 9f3592b02..3985e20fd 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileReader.cs @@ -166,10 +166,39 @@ namespace Microsoft.DotNet.ProjectModel.Graph library.CompileTimeAssemblies = ReadObject(jobject.ValueAsJsonObject("compile"), ReadFileItem); library.ResourceAssemblies = ReadObject(jobject.ValueAsJsonObject("resource"), ReadFileItem); library.NativeLibraries = ReadObject(jobject.ValueAsJsonObject("native"), ReadFileItem); - + library.ContentFiles = ReadObject(jobject.ValueAsJsonObject("contentFiles"), ReadContentFile); return library; } + private static LockFileContentFile ReadContentFile(string property, JsonValue json) + { + var contentFile = new LockFileContentFile() + { + Path = property + }; + + var jsonObject = json as JsonObject; + if (jsonObject != null) + { + + BuildAction action; + BuildAction.TryParse(jsonObject.ValueAsString("buildAction"), out action); + + contentFile.BuildAction = action; + var codeLanguage = jsonObject.ValueAsString("codeLanguage"); + if (codeLanguage == "any") + { + codeLanguage = null; + } + contentFile.CodeLanguage = codeLanguage; + contentFile.OutputPath = jsonObject.ValueAsString("outputPath"); + contentFile.PPOutputPath = jsonObject.ValueAsString("ppOutputPath"); + contentFile.CopyToOutput = ReadBool(jsonObject, "copyToOutput", false); + } + + return contentFile; + } + private static ProjectFileDependencyGroup ReadProjectFileDependencyGroup(string property, JsonValue json) { return new ProjectFileDependencyGroup( diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetContentFile.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetContentFile.cs new file mode 100644 index 000000000..72eecd0c1 --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetContentFile.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.ProjectModel.Graph +{ + public class LockFileContentFile + { + public string Path { get; set; } + + public string OutputPath { get; set; } + + public string PPOutputPath { get; set; } + + public BuildAction BuildAction { get; set; } + + public string CodeLanguage { get; set; } + + public bool CopyToOutput { get; set; } = false; + } +} \ No newline at end of file diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetLibrary.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetLibrary.cs index 7339fed39..eb593e3a4 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetLibrary.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LockFileTargetLibrary.cs @@ -30,5 +30,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph public IList ResourceAssemblies { get; set; } = new List(); public IList NativeLibraries { get; set; } = new List(); + + public IList ContentFiles { get; set; } = new List(); } } diff --git a/src/Microsoft.DotNet.ProjectModel/PackageDescription.cs b/src/Microsoft.DotNet.ProjectModel/PackageDescription.cs index b66d8a941..5327a4896 100644 --- a/src/Microsoft.DotNet.ProjectModel/PackageDescription.cs +++ b/src/Microsoft.DotNet.ProjectModel/PackageDescription.cs @@ -42,6 +42,8 @@ namespace Microsoft.DotNet.ProjectModel public IEnumerable NativeLibraries => Target.NativeLibraries; + public IEnumerable ContentFiles => Target.ContentFiles; + private IEnumerable FilterPlaceholders(IList items) { return items.Where(a => !PackageDependencyProvider.IsPlaceholderFile(a)); diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectExtensions.cs b/src/Microsoft.DotNet.ProjectModel/ProjectExtensions.cs new file mode 100644 index 000000000..2c5d9c69c --- /dev/null +++ b/src/Microsoft.DotNet.ProjectModel/ProjectExtensions.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.ProjectModel +{ + public static class ProjectExtensions + { + private static readonly KeyValuePair[] _compilerNameToLanguageId = + { + new KeyValuePair("csc", "cs"), + new KeyValuePair("vbc", "vb"), + new KeyValuePair("fsc", "fs") + }; + + public static string GetSourceCodeLanguage(this Project project) + { + foreach (var kvp in _compilerNameToLanguageId) + { + if (kvp.Key == project.CompilerName) + { + return kvp.Value; + } + } + return null; + } + } +} diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs index 744023c1a..1336d21dc 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectReader.cs @@ -141,7 +141,7 @@ namespace Microsoft.DotNet.ProjectModel project.ProjectUrl = rawProject.ValueAsString("projectUrl"); project.LicenseUrl = rawProject.ValueAsString("licenseUrl"); project.IconUrl = rawProject.ValueAsString("iconUrl"); - project.CompilerName = rawProject.ValueAsString("compilerName"); + project.CompilerName = rawProject.ValueAsString("compilerName") ?? "csc"; project.TestRunner = rawProject.ValueAsString("testRunner"); project.Authors = rawProject.ValueAsStringArray("authors") ?? EmptyArray.Value; diff --git a/src/dotnet/commands/dotnet-build/CompileContext.cs b/src/dotnet/commands/dotnet-build/CompileContext.cs index 91010bb3b..e66a08fd6 100644 --- a/src/dotnet/commands/dotnet-build/CompileContext.cs +++ b/src/dotnet/commands/dotnet-build/CompileContext.cs @@ -255,7 +255,7 @@ namespace Microsoft.DotNet.Tools.Build private void CollectCompilerNamePreconditions(ProjectContext project, IncrementalPreconditions preconditions) { - var projectCompiler = CompilerUtil.ResolveCompilerName(project); + var projectCompiler = project.ProjectFile.CompilerName; if (!KnownCompilers.Any(knownCompiler => knownCompiler.Equals(projectCompiler, StringComparison.Ordinal))) { diff --git a/src/dotnet/commands/dotnet-compile/CompilerUtil.cs b/src/dotnet/commands/dotnet-compile/CompilerUtil.cs index bc7f13a32..dda381ee7 100644 --- a/src/dotnet/commands/dotnet-compile/CompilerUtil.cs +++ b/src/dotnet/commands/dotnet-compile/CompilerUtil.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; - using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.Cli.Compiler.Common; using Microsoft.DotNet.ProjectModel.Compilation; @@ -20,34 +19,12 @@ namespace Microsoft.DotNet.Tools.Compiler { public static class CompilerUtil { - public static string ResolveCompilerName(ProjectContext context) - { - var compilerName = context.ProjectFile.CompilerName; - compilerName = compilerName ?? "csc"; - - return compilerName; - } - - private static readonly KeyValuePair[] s_compilerNameToLanguageId = - { - new KeyValuePair("csc", "cs"), - new KeyValuePair("vbc", "vb"), - new KeyValuePair("fsc", "fs") - }; - public static string ResolveLanguageId(ProjectContext context) { var languageId = context.ProjectFile.AnalyzerOptions?.LanguageId; if (languageId == null) { - var compilerName = ResolveCompilerName(context); - foreach (var kvp in s_compilerNameToLanguageId) - { - if (kvp.Key == compilerName) - { - languageId = kvp.Value; - } - } + languageId = context.ProjectFile.GetSourceCodeLanguage(); } return languageId; @@ -166,7 +143,7 @@ namespace Microsoft.DotNet.Tools.Compiler //used in incremental precondition checks public static IEnumerable GetCommandsInvokedByCompile(ProjectContext project) { - return new List {ResolveCompilerName(project), "compile"}; + return new List {project.ProjectFile.CompilerName, "compile"}; } } } \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs index 960b2b97a..674542dca 100644 --- a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs +++ b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.DotNet.Cli.Compiler.Common; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.ProjectModel; +using Microsoft.DotNet.ProjectModel.Compilation; using Microsoft.DotNet.ProjectModel.Utilities; using Microsoft.Extensions.DependencyModel; @@ -90,7 +91,15 @@ namespace Microsoft.DotNet.Tools.Compiler foreach (var dependency in dependencies) { references.AddRange(dependency.CompilationAssemblies.Select(r => r.ResolvedPath)); - compilerArgs.AddRange(dependency.SourceReferences.Select(s => $"{s}")); + + compilerArgs.AddRange(dependency.SourceReferences.Select(s => s.GetTransformedFile(intermediateOutputPath))); + + foreach (var resourceFile in dependency.EmbeddedResources) + { + var transformedResource = resourceFile.GetTransformedFile(intermediateOutputPath); + var resourceName = ResourceManifestName.CreateManifestName(Path.GetFileName(resourceFile.ResolvedPath), context.ProjectFile.Name); + compilerArgs.Add($"--resource:\"{transformedResource}\",{resourceName}"); + } // Add analyzer references compilerArgs.AddRange(dependency.AnalyzerReferences @@ -126,7 +135,7 @@ namespace Microsoft.DotNet.Tools.Compiler var sourceFiles = CompilerUtil.GetCompilationSources(context); compilerArgs.AddRange(sourceFiles); - var compilerName = CompilerUtil.ResolveCompilerName(context); + var compilerName = context.ProjectFile.CompilerName; // Write RSP file var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.rsp"); diff --git a/src/dotnet/commands/dotnet-projectmodel-server/Helpers/ProjectExtensions.cs b/src/dotnet/commands/dotnet-projectmodel-server/Helpers/ProjectExtensions.cs index c7eb9b30e..ef28bbc23 100644 --- a/src/dotnet/commands/dotnet-projectmodel-server/Helpers/ProjectExtensions.cs +++ b/src/dotnet/commands/dotnet-projectmodel-server/Helpers/ProjectExtensions.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.IO; diff --git a/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectContextSnapshot.cs b/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectContextSnapshot.cs index 167115a64..195bf5ba1 100644 --- a/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectContextSnapshot.cs +++ b/src/dotnet/commands/dotnet-projectmodel-server/InternalModels/ProjectContextSnapshot.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.ProjectModel.Compilation; using Microsoft.DotNet.ProjectModel.Server.Helpers; using Microsoft.DotNet.ProjectModel.Server.Models; using Microsoft.DotNet.Cli.Compiler.Common; @@ -45,7 +46,7 @@ namespace Microsoft.DotNet.ProjectModel.Server // both will be listed as dependencies. Prefix "fx/" will be added to ReferenceAssembly type dependency. foreach (var export in allExports.Values) { - allSourceFiles.AddRange(export.SourceReferences); + allSourceFiles.AddRange(export.SourceReferences.Select(f => f.ResolvedPath)); allFileReferences.AddRange(export.CompilationAssemblies.Select(asset => asset.ResolvedPath)); var diagnostics = diagnosticsLookup[export.Library].ToList(); diff --git a/src/dotnet/commands/dotnet-publish/PublishCommand.cs b/src/dotnet/commands/dotnet-publish/PublishCommand.cs index 0f0f2316a..ffd8f4988 100644 --- a/src/dotnet/commands/dotnet-publish/PublishCommand.cs +++ b/src/dotnet/commands/dotnet-publish/PublishCommand.cs @@ -87,10 +87,11 @@ namespace Microsoft.DotNet.Tools.Publish Reporter.Output.WriteLine($"Publishing {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}/{context.RuntimeIdentifier.Yellow()}"); var options = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration); + var outputPaths = context.GetOutputPaths(configuration, buildBasePath, outputPath); if (string.IsNullOrEmpty(outputPath)) { - outputPath = Path.Combine(context.GetOutputPaths(configuration, buildBasePath, outputPath).RuntimeOutputPath, PublishSubfolderName); + outputPath = Path.Combine(outputPaths.RuntimeOutputPath, PublishSubfolderName); } var contextVariables = new Dictionary @@ -148,7 +149,7 @@ namespace Microsoft.DotNet.Tools.Publish PublishFiles(export.RuntimeAssemblies, outputPath, nativeSubdirectories: false); PublishFiles(export.NativeLibraries, outputPath, nativeSubdirectories); - export.RuntimeAssets.StructuredCopyTo(outputPath); + export.RuntimeAssets.StructuredCopyTo(outputPath, outputPaths.IntermediateOutputDirectoryPath); if (options.PreserveCompilationContext.GetValueOrDefault()) { diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/LibraryExporterPackageTests.cs b/test/Microsoft.DotNet.ProjectModel.Tests/LibraryExporterPackageTests.cs new file mode 100644 index 000000000..1fe4cbb7b --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/LibraryExporterPackageTests.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.DotNet.ProjectModel.Compilation; +using Microsoft.DotNet.ProjectModel.Graph; +using Microsoft.DotNet.ProjectModel.Resolution; +using Microsoft.DotNet.Tools.Test.Utilities; +using FluentAssertions; +using Xunit; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class LibraryExporterPackageTests + { + private const string PackagePath = "PackagePath"; + + private LibraryExport ExportSingle(LibraryDescription description = null) + { + var rootProject = new Project() + { + Name = "RootProject", + CompilerName = "csc" + }; + + var rootProjectDescription = new ProjectDescription( + new LibraryRange(), + rootProject, + new LibraryRange[] { }, + new TargetFrameworkInformation(), + true); + + if (description == null) + { + description = rootProjectDescription; + } + else + { + description.Parents.Add(rootProjectDescription); + } + + var libraryManager = new LibraryManager(new[] { description }, new DiagnosticMessage[] { }, ""); + var allExports = new LibraryExporter(rootProjectDescription, libraryManager, "config", "runtime", "basepath", "solutionroot").GetAllExports(); + var export = allExports.Single(); + return export; + } + + private PackageDescription CreateDescription(LockFileTargetLibrary target = null, LockFilePackageLibrary package = null) + { + return new PackageDescription(PackagePath, + package ?? new LockFilePackageLibrary(), + target ?? new LockFileTargetLibrary(), + new List(), true); + } + + [Fact] + private void ExportsPackageNativeLibraries() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + NativeLibraries = new List() + { + { new LockFileItem() { Path = "lib/Native.so" } } + } + }); + + var result = ExportSingle(description); + result.NativeLibraries.Should().HaveCount(1); + + var libraryAsset = result.NativeLibraries.First(); + libraryAsset.Name.Should().Be("Native"); + libraryAsset.Transform.Should().BeNull(); + libraryAsset.RelativePath.Should().Be("lib/Native.so"); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "lib/Native.so")); + } + + [Fact] + private void ExportsPackageCompilationAssebmlies() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + CompileTimeAssemblies = new List() + { + { new LockFileItem() { Path = "ref/Native.dll" } } + } + }); + + var result = ExportSingle(description); + result.CompilationAssemblies.Should().HaveCount(1); + + var libraryAsset = result.CompilationAssemblies.First(); + libraryAsset.Name.Should().Be("Native"); + libraryAsset.Transform.Should().BeNull(); + libraryAsset.RelativePath.Should().Be("ref/Native.dll"); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "ref/Native.dll")); + } + + [Fact] + private void ExportsPackageRuntimeAssebmlies() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + RuntimeAssemblies = new List() + { + { new LockFileItem() { Path = "ref/Native.dll" } } + } + }); + + var result = ExportSingle(description); + result.RuntimeAssemblies.Should().HaveCount(1); + + var libraryAsset = result.RuntimeAssemblies.First(); + libraryAsset.Name.Should().Be("Native"); + libraryAsset.Transform.Should().BeNull(); + libraryAsset.RelativePath.Should().Be("ref/Native.dll"); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "ref/Native.dll")); + } + + [Fact] + private void ExportsSources() + { + var description = CreateDescription( + package: new LockFilePackageLibrary() + { + Files = new List() + { + Path.Combine("shared", "file.cs") + } + }); + + var result = ExportSingle(description); + result.SourceReferences.Should().HaveCount(1); + + var libraryAsset = result.SourceReferences.First(); + libraryAsset.Name.Should().Be("file"); + libraryAsset.Transform.Should().BeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("shared", "file.cs")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "shared", "file.cs")); + } + + [Fact] + private void ExportsCopyToOutputContentFiles() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + ContentFiles = new List() + { + new LockFileContentFile() + { + CopyToOutput = true, + Path = Path.Combine("content", "file.txt"), + OutputPath = Path.Combine("Out","Path.txt"), + PPOutputPath = "something" + } + } + }); + + var result = ExportSingle(description); + result.RuntimeAssets.Should().HaveCount(1); + + var libraryAsset = result.RuntimeAssets.First(); + libraryAsset.Transform.Should().NotBeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("Out", "Path.txt")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "content", "file.txt")); + } + + + [Fact] + private void ExportsResourceContentFiles() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + ContentFiles = new List() + { + new LockFileContentFile() + { + BuildAction = BuildAction.EmbeddedResource, + Path = Path.Combine("content", "file.txt"), + PPOutputPath = "something" + } + } + }); + + var result = ExportSingle(description); + result.EmbeddedResources.Should().HaveCount(1); + + var libraryAsset = result.EmbeddedResources.First(); + libraryAsset.Transform.Should().NotBeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("content", "file.txt")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "content", "file.txt")); + } + + [Fact] + private void ExportsCompileContentFiles() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + ContentFiles = new List() + { + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.cs"), + PPOutputPath = "something" + } + } + }); + + var result = ExportSingle(description); + result.SourceReferences.Should().HaveCount(1); + + var libraryAsset = result.SourceReferences.First(); + libraryAsset.Transform.Should().NotBeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("content", "file.cs")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "content", "file.cs")); + } + + + + [Fact] + private void SelectsContentFilesOfProjectCodeLanguage() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + ContentFiles = new List() + { + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.cs"), + PPOutputPath = "something", + CodeLanguage = "cs" + }, + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.vb"), + PPOutputPath = "something", + CodeLanguage = "vb" + }, + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.any"), + PPOutputPath = "something", + } + } + }); + + var result = ExportSingle(description); + result.SourceReferences.Should().HaveCount(1); + + var libraryAsset = result.SourceReferences.First(); + libraryAsset.Transform.Should().NotBeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("content", "file.cs")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "content", "file.cs")); + } + + [Fact] + private void SelectsContentFilesWithNoLanguageIfProjectLanguageNotMathed() + { + var description = CreateDescription( + new LockFileTargetLibrary() + { + ContentFiles = new List() + { + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.vb"), + PPOutputPath = "something", + CodeLanguage = "vb" + }, + new LockFileContentFile() + { + BuildAction = BuildAction.Compile, + Path = Path.Combine("content", "file.any"), + PPOutputPath = "something", + } + } + }); + + var result = ExportSingle(description); + result.SourceReferences.Should().HaveCount(1); + + var libraryAsset = result.SourceReferences.First(); + libraryAsset.Transform.Should().NotBeNull(); + libraryAsset.RelativePath.Should().Be(Path.Combine("content", "file.any")); + libraryAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "content", "file.any")); + } + } +} diff --git a/test/Microsoft.DotNet.ProjectModel.Tests/PreprocessorTests.cs b/test/Microsoft.DotNet.ProjectModel.Tests/PreprocessorTests.cs new file mode 100644 index 000000000..5a0fec8db --- /dev/null +++ b/test/Microsoft.DotNet.ProjectModel.Tests/PreprocessorTests.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; +using Microsoft.DotNet.Tools.Compiler; + +namespace Microsoft.DotNet.ProjectModel.Tests +{ + public class PreprocessorTests + { + private string Preprocess(string text, IDictionary parameters) + { + using (var input = new MemoryStream(Encoding.UTF8.GetBytes(text))) + { + using (var output = new MemoryStream()) + { + PPFilePreprocessor.Preprocess(input, output, parameters); + return Encoding.UTF8.GetString(output.ToArray()); + } + } + } + + [Theory] + [InlineData("$a$", "AValue")] + [InlineData("$a", "$a")] + [InlineData("a$", "a$")] + [InlineData("$$a$", "$a$")] + [InlineData("$a$$b$", "AValueBValue")] + [InlineData("$$a$$$$b$$", "$a$$b$")] + + public void ProcessesCorrectly(string input, string output) + { + var parameters = new Dictionary() + { + { "a", "AValue" }, + { "b", "BValue" } + }; + var result = Preprocess(input, parameters); + result.Should().Be(output); + } + + [Fact] + public void ThrowsOnParameterNotFound() + { + var ex = Assert.Throws(() => Preprocess("$a$", new Dictionary())); + ex.Message.Should().Contain("a"); + } + } +} diff --git a/test/dotnet-compile.Tests/CompilerTests.cs b/test/dotnet-compile.Tests/CompilerTests.cs index c0ed48edb..4c4057e7e 100644 --- a/test/dotnet-compile.Tests/CompilerTests.cs +++ b/test/dotnet-compile.Tests/CompilerTests.cs @@ -3,7 +3,9 @@ using System; using System.IO; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Test.Utilities; +using FluentAssertions; using Xunit; namespace Microsoft.DotNet.Tools.Compiler.Tests @@ -106,6 +108,44 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests buildCommand.Execute().Should().Pass(); } + [Fact] + public void ContentFilesAreCopied() + { + var testInstance = TestAssetsManager.CreateTestInstance("TestAppWithContentPackage") + .WithLockFiles(); + + var root = testInstance.TestRoot; + + // run compile + var outputDir = Path.Combine(root, "bin"); + var testProject = ProjectUtils.GetProjectJson(root, "TestAppWithContentPackage"); + var buildCommand = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework); + var result = buildCommand.ExecuteWithCapturedOutput(); + result.Should().Pass(); + + result = Command.Create(Path.Combine(outputDir, buildCommand.GetOutputExecutableName()), new string [0]) + .CaptureStdErr() + .CaptureStdOut() + .Execute(); + result.Should().Pass(); + + // verify the output xml file + new DirectoryInfo(outputDir).Sub("scripts").Should() + .Exist() + .And.HaveFile("run.cmd"); + new DirectoryInfo(outputDir).Should() + .HaveFile("config.xml"); + // verify embedded resources + result.StdOut.Should().Contain("TestAppWithContentPackage.dnf.png"); + result.StdOut.Should().Contain("TestAppWithContentPackage.ui.png"); + // verify 'all' language files not included + result.StdOut.Should().NotContain("TestAppWithContentPackage.dnf_all.png"); + result.StdOut.Should().NotContain("TestAppWithContentPackage.ui_all.png"); + // verify classes + result.StdOut.Should().Contain("TestAppWithContentPackage.Foo"); + result.StdOut.Should().Contain("MyNamespace.Util"); + } + private void CopyProjectToTempDir(string projectDir, TempDirectory tempDir) { // copy all the files to temp dir