Merge pull request #2493 from dotnet/ajbaaska/project-model-fix

Updates to project.json schema
This commit is contained in:
Ajay Bhargav Baaskaran 2016-04-27 12:32:29 -07:00
commit 563f6497fd
49 changed files with 1600 additions and 277 deletions

View file

@ -0,0 +1,13 @@
using System;
namespace ConsoleApplication
{
public class Program
{
public static int Main(string[] args)
{
Console.WriteLine("Hello World!");
return 100;
}
}
}

View file

@ -0,0 +1,31 @@
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true,
"embed": {
"include": "*.resx",
"mappings": {
"myresource.resx": "resource2.resx"
}
},
"copyToOutput": {
"include": "copy/*.txt",
"excludeFiles": "copy/fileex.txt"
}
},
"dependencies": {
"Microsoft.NETCore.App": "1.0.0-rc2-*"
},
"packOptions": {
"files": {
"includeFiles": "packfiles/pack1.txt",
"mappings": {
"newpath/": "packfiles/pack2.txt"
}
}
},
"publishOptions": "testpublishfile.txt",
"frameworks": {
"netcoreapp1.0": { }
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
Some content
</root>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
Some content
</root>

View file

@ -6,17 +6,17 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Files;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.Tools.Common;
using Microsoft.Extensions.DependencyModel;
using NuGet.Frameworks;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Reflection.PortableExecutable;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.Cli.Compiler.Common
{
@ -88,7 +88,20 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
private void CopyContentFiles()
{
var contentFiles = new ContentFiles(_context);
contentFiles.StructuredCopyTo(_runtimeOutputPath);
if (_compilerOptions.CopyToOutputInclude != null)
{
var includeEntries = IncludeFilesResolver.GetIncludeFiles(
_compilerOptions.CopyToOutputInclude,
PathUtility.EnsureTrailingSlash(_runtimeOutputPath),
diagnostics: null);
contentFiles.StructuredCopyTo(_runtimeOutputPath, includeEntries);
}
else
{
contentFiles.StructuredCopyTo(_runtimeOutputPath);
}
}
private void CopyAssemblies(IEnumerable<LibraryExport> libraryExports)

View file

@ -22,7 +22,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
var baseOption = context.ProjectFile.GetCompilerOptions(framework, configurationName);
IReadOnlyList<string> defaultSuppresses;
var compilerName = context.ProjectFile.CompilerName ?? "csc";
var compilerName = baseOption.CompilerName ?? "csc";
if (DefaultCompilerWarningSuppresses.Suppresses.TryGetValue(compilerName, out defaultSuppresses))
{
baseOption.SuppressWarnings = (baseOption.SuppressWarnings ?? Enumerable.Empty<string>()).Concat(defaultSuppresses).Distinct();
@ -46,22 +46,22 @@ namespace Microsoft.DotNet.Cli.Compiler.Common
// used in incremental compilation for the key file
public static CommonCompilerOptions ResolveCompilationOptions(this ProjectContext context, string configuration)
{
var compilationOptions = context.GetLanguageSpecificCompilerOptions(context.TargetFramework, configuration);
var compilerOptions = context.GetLanguageSpecificCompilerOptions(context.TargetFramework, configuration);
// Path to strong naming key in environment variable overrides path in project.json
var environmentKeyFile = Environment.GetEnvironmentVariable(EnvironmentNames.StrongNameKeyFile);
if (!string.IsNullOrWhiteSpace(environmentKeyFile))
{
compilationOptions.KeyFile = environmentKeyFile;
compilerOptions.KeyFile = environmentKeyFile;
}
else if (!string.IsNullOrWhiteSpace(compilationOptions.KeyFile))
else if (!string.IsNullOrWhiteSpace(compilerOptions.KeyFile))
{
// Resolve full path to key file
compilationOptions.KeyFile =
Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilationOptions.KeyFile));
compilerOptions.KeyFile =
Path.GetFullPath(Path.Combine(context.ProjectFile.ProjectDirectory, compilerOptions.KeyFile));
}
return compilationOptions;
return compilerOptions;
}
}
}

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.Tools.Common;
namespace Microsoft.DotNet.Files
@ -60,6 +61,29 @@ namespace Microsoft.DotNet.Files
RemoveAttributeFromFiles(pathMap.Values, FileAttributes.ReadOnly);
}
public void StructuredCopyTo(string targetDirectory, IEnumerable<IncludeEntry> includeEntries)
{
if (includeEntries == null)
{
return;
}
foreach (var targetDir in includeEntries
.Select(f => Path.GetDirectoryName(f.TargetPath))
.Distinct()
.Where(t => !Directory.Exists(t)))
{
Directory.CreateDirectory(targetDir);
}
foreach (var file in includeEntries)
{
File.Copy(file.SourcePath, file.TargetPath, overwrite: true);
}
RemoveAttributeFromFiles(includeEntries.Select(f => f.TargetPath), FileAttributes.ReadOnly);
}
private static void RemoveAttributeFromFiles(IEnumerable<string> files, FileAttributes attribute)
{
foreach (var file in files)

View file

@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
@ -69,15 +70,26 @@ namespace Microsoft.DotNet.ProjectModel.Workspaces
// TODO: ctor argument?
var configuration = "Debug";
var compilationOptions = project.GetLanguageSpecificCompilerOptions(project.TargetFramework, configuration);
var compilerOptions = project.GetLanguageSpecificCompilerOptions(project.TargetFramework, configuration);
var compilationSettings = ToCompilationSettings(compilationOptions, project.TargetFramework, project.ProjectFile.ProjectDirectory);
var compilationSettings = ToCompilationSettings(compilerOptions, project.TargetFramework, project.ProjectFile.ProjectDirectory);
OnParseOptionsChanged(projectInfo.Id, new CSharpParseOptions(compilationSettings.LanguageVersion, preprocessorSymbols: compilationSettings.Defines));
OnCompilationOptionsChanged(projectInfo.Id, compilationSettings.CompilationOptions);
foreach (var file in project.ProjectFile.Files.SourceFiles)
IEnumerable<string> sourceFiles = null;
if (compilerOptions.CompileInclude == null)
{
sourceFiles = project.ProjectFile.Files.SourceFiles;
}
else
{
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
sourceFiles = includeFiles.Select(f => f.SourcePath);
}
foreach (var file in sourceFiles)
{
using (var stream = File.OpenRead(file))
{

View file

@ -1,9 +1,9 @@
// 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 Microsoft.DotNet.ProjectModel.Files;
namespace Microsoft.DotNet.ProjectModel
{
@ -39,7 +39,15 @@ namespace Microsoft.DotNet.ProjectModel
public IEnumerable<string> AdditionalArguments { get; set; }
public string OutputName { get;set; }
public string OutputName { get; set; }
public string CompilerName { get; set; }
public IncludeContext CompileInclude { get; set; }
public IncludeContext EmbedInclude { get; set; }
public IncludeContext CopyToOutputInclude { get; set; }
public override bool Equals(object obj)
{
@ -60,7 +68,21 @@ namespace Microsoft.DotNet.ProjectModel
EnumerableEquals(Defines, other.Defines) &&
EnumerableEquals(SuppressWarnings, other.SuppressWarnings) &&
EnumerableEquals(AdditionalArguments, other.AdditionalArguments) &&
OutputName == other.OutputName;
OutputName == other.OutputName &&
CompilerName == other.CompilerName &&
IsEqual(CompileInclude, other.CompileInclude) &&
IsEqual(EmbedInclude, other.EmbedInclude) &&
IsEqual(CopyToOutputInclude, other.CopyToOutputInclude);
}
private static bool IsEqual(IncludeContext first, IncludeContext second)
{
if (first == null || second == null)
{
return first == second;
}
return first.Equals(second);
}
private static bool EnumerableEquals(IEnumerable<string> left, IEnumerable<string> right)
@ -161,6 +183,27 @@ namespace Microsoft.DotNet.ProjectModel
{
result.OutputName = option.OutputName;
}
if (option.CompileInclude != null)
{
result.CompileInclude = option.CompileInclude;
}
if (option.EmbedInclude != null)
{
result.EmbedInclude = option.EmbedInclude;
}
if (option.CopyToOutputInclude != null)
{
result.CopyToOutputInclude = option.CopyToOutputInclude;
}
// compilerName set in the root cannot be overriden.
if (result.CompilerName == null)
{
result.CompilerName = option.CompilerName;
}
}
return result;

View file

@ -279,6 +279,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
private LibraryExport ExportProject(ProjectDescription project)
{
var builder = LibraryExportBuilder.Create(project);
var compilerOptions = project.Project.GetCompilerOptions(project.TargetFrameworkInfo.FrameworkName, _configuration);
if (!string.IsNullOrEmpty(project.TargetFrameworkInfo?.AssemblyPath))
{
@ -298,7 +299,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
builder.AddRuntimeAsset(new LibraryAsset(Path.GetFileName(pdbPath), Path.GetFileName(pdbPath), pdbPath));
}
}
else if (project.Project.Files.SourceFiles.Any())
else if (HasSourceFiles(project))
{
var outputPaths = project.GetOutputPaths(_buildBasePath, _solutionRootPath, _configuration, _runtime);
@ -336,6 +337,20 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
return builder.Build();
}
private bool HasSourceFiles(ProjectDescription project)
{
var compilerOptions = project.TargetFrameworkInfo.CompilerOptions;
if (compilerOptions.CompileInclude == null)
{
return project.Project.Files.SourceFiles.Any();
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
return includeFiles.Any();
}
private IEnumerable<LibraryAsset> CollectAssets(CompilationOutputFiles files)
{
var assemblyPath = files.Assembly;

View file

@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Resources;
using NuGet.Frameworks;
@ -27,8 +28,8 @@ namespace Microsoft.DotNet.ProjectModel
Framework = framework;
OutputExtension = FileNameSuffixes.DotNet.DynamicLib;
var compilationOptions = Project.GetCompilerOptions(framework, configuration);
if (framework.IsDesktop() && compilationOptions.EmitEntryPoint.GetValueOrDefault())
var compilerOptions = Project.GetCompilerOptions(framework, configuration);
if (framework.IsDesktop() && compilerOptions.EmitEntryPoint.GetValueOrDefault())
{
OutputExtension = FileNameSuffixes.DotNet.Exe;
}
@ -40,9 +41,9 @@ namespace Microsoft.DotNet.ProjectModel
{
get
{
var compilationOptions = Project.GetCompilerOptions(Framework, Configuration);
var compilerOptions = Project.GetCompilerOptions(Framework, Configuration);
return Path.Combine(BasePath, compilationOptions.OutputName + OutputExtension);
return Path.Combine(BasePath, compilerOptions.OutputName + OutputExtension);
}
}
@ -58,14 +59,17 @@ namespace Microsoft.DotNet.ProjectModel
public virtual IEnumerable<ResourceFile> Resources()
{
var resourceNames = Project.Files.ResourceFiles
.Select(f => ResourceUtility.GetResourceCultureName(f.Key))
var resourceCultureNames = GetResourceFiles()
.Select(f => ResourceUtility.GetResourceCultureName(f))
.Where(f => !string.IsNullOrEmpty(f))
.Distinct();
foreach (var resourceName in resourceNames)
foreach (var resourceCultureName in resourceCultureNames)
{
yield return new ResourceFile(Path.Combine(BasePath, resourceName, Project.Name + ".resources" + FileNameSuffixes.DotNet.DynamicLib), resourceName);
yield return new ResourceFile(
Path.Combine(
BasePath, resourceCultureName, Project.Name + ".resources" + FileNameSuffixes.DotNet.DynamicLib),
resourceCultureName);
}
}
@ -73,8 +77,8 @@ namespace Microsoft.DotNet.ProjectModel
{
yield return Assembly;
yield return PdbPath;
var compilationOptions = Project.GetCompilerOptions(Framework, Configuration);
if (compilationOptions.GenerateXmlDocumentation == true)
var compilerOptions = Project.GetCompilerOptions(Framework, Configuration);
if (compilerOptions.GenerateXmlDocumentation == true)
{
yield return Path.ChangeExtension(Assembly, "xml");
}
@ -83,5 +87,18 @@ namespace Microsoft.DotNet.ProjectModel
yield return resource.Path;
}
}
private IEnumerable<string> GetResourceFiles()
{
var compilerOptions = Project.GetCompilerOptions(Framework, Configuration);
if (compilerOptions.EmbedInclude == null)
{
return Project.Files.ResourceFiles.Keys;
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.EmbedInclude, "/", diagnostics: null);
return includeFiles.Select(f => f.SourcePath);
}
}
}

View file

@ -16,5 +16,11 @@ namespace Microsoft.DotNet.ProjectModel
// Failed to read lock file
public static readonly string DOTNET1014 = nameof(DOTNET1014);
// The '{0}' option is deprecated. Use '{1}' instead.
public static readonly string DOTNET1015 = nameof(DOTNET1015);
// The '{0}' option in the root is deprecated. Use it in '{1}' instead.
public static readonly string DOTNET1016 = nameof(DOTNET1016);
}
}

View file

@ -0,0 +1,215 @@
// 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;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectModel.Files
{
public class IncludeContext
{
private static readonly char[] PatternSeparator = new[] { ';' };
public IncludeContext(
string sourceBasePath,
string option,
JObject rawObject,
string[] defaultBuiltInInclude,
string[] defaultBuiltInExclude)
{
if (sourceBasePath == null)
{
throw new ArgumentNullException(nameof(sourceBasePath));
}
if (option == null)
{
throw new ArgumentNullException(nameof(option));
}
if (rawObject == null)
{
throw new ArgumentNullException(nameof(rawObject));
}
SourceBasePath = sourceBasePath;
Option = option;
var token = rawObject.Value<JToken>(option);
if (token.Type != JTokenType.Object)
{
IncludePatterns = new List<string>(ExtractValues(token));
}
else
{
IncludePatterns = CreateCollection(
sourceBasePath, "include", ExtractValues(token.Value<JToken>("include")), literalPath: false);
ExcludePatterns = CreateCollection(
sourceBasePath, "exclude", ExtractValues(token.Value<JToken>("exclude")), literalPath: false);
IncludeFiles = CreateCollection(
sourceBasePath, "includeFiles", ExtractValues(token.Value<JToken>("includeFiles")), literalPath: true);
ExcludeFiles = CreateCollection(
sourceBasePath, "excludeFiles", ExtractValues(token.Value<JToken>("excludeFiles")), literalPath: true);
var builtIns = token.Value<JToken>("builtIns") as JObject;
if (builtIns != null)
{
BuiltInsInclude = CreateCollection(
sourceBasePath, "include", ExtractValues(builtIns.Value<JToken>("include")), literalPath: false);
if (defaultBuiltInInclude != null && !BuiltInsInclude.Any())
{
BuiltInsInclude = defaultBuiltInInclude.ToList();
}
BuiltInsExclude = CreateCollection(
sourceBasePath, "exclude", ExtractValues(builtIns.Value<JToken>("exclude")), literalPath: false);
if (defaultBuiltInExclude != null && !BuiltInsExclude.Any())
{
BuiltInsExclude = defaultBuiltInExclude.ToList();
}
}
var mappings = token.Value<JToken>("mappings") as JObject;
if (mappings != null)
{
Mappings = new Dictionary<string, IncludeContext>();
foreach (var map in mappings)
{
Mappings.Add(
map.Key,
new IncludeContext(
sourceBasePath,
map.Key,
mappings,
defaultBuiltInInclude: null,
defaultBuiltInExclude: null));
}
}
}
}
public string SourceBasePath { get; }
public string Option { get; }
public List<string> IncludePatterns { get; }
public List<string> ExcludePatterns { get; }
public List<string> IncludeFiles { get; }
public List<string> ExcludeFiles { get; }
public List<string> BuiltInsInclude { get; }
public List<string> BuiltInsExclude { get; }
public IDictionary<string, IncludeContext> Mappings { get; }
public override bool Equals(object obj)
{
var other = obj as IncludeContext;
return other != null &&
SourceBasePath == other.SourceBasePath &&
Option == other.Option &&
EnumerableEquals(IncludePatterns, other.IncludePatterns) &&
EnumerableEquals(ExcludePatterns, other.ExcludePatterns) &&
EnumerableEquals(IncludeFiles, other.IncludeFiles) &&
EnumerableEquals(ExcludeFiles, other.ExcludeFiles) &&
EnumerableEquals(BuiltInsInclude, other.BuiltInsInclude) &&
EnumerableEquals(BuiltInsExclude, other.BuiltInsExclude) &&
EnumerableEquals(Mappings, other.Mappings);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
private static bool EnumerableEquals<T>(IEnumerable<T> left, IEnumerable<T> right)
=> Enumerable.SequenceEqual(left ?? EmptyArray<T>.Value, right ?? EmptyArray<T>.Value);
private static string[] ExtractValues(JToken token)
{
if (token != null)
{
if (token.Type == JTokenType.String)
{
return new string[] { token.Value<string>() };
}
else if (token.Type == JTokenType.Array)
{
return token.Values<string>().ToArray();
}
}
return new string[0];
}
internal static List<string> CreateCollection(
string projectDirectory,
string propertyName,
IEnumerable<string> patternsStrings,
bool literalPath)
{
var patterns = patternsStrings
.SelectMany(patternsString => GetSourcesSplit(patternsString))
.Select(patternString =>
patternString.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar));
foreach (var pattern in patterns)
{
if (Path.IsPathRooted(pattern))
{
throw new InvalidOperationException($"The '{propertyName}' property cannot be a rooted path.");
}
if (literalPath && pattern.Contains('*'))
{
throw new InvalidOperationException($"The '{propertyName}' property cannot contain wildcard characters.");
}
}
return new List<string>(patterns.Select(pattern => FolderToPattern(pattern, projectDirectory)));
}
private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
{
if (string.IsNullOrEmpty(sourceDescription))
{
return Enumerable.Empty<string>();
}
return sourceDescription.Split(PatternSeparator, StringSplitOptions.RemoveEmptyEntries);
}
private static string FolderToPattern(string candidate, string projectDir)
{
// If it's already a pattern, no change is needed
if (candidate.Contains('*'))
{
return candidate;
}
// If the given string ends with a path separator, or it is an existing directory
// we convert this folder name to a pattern matching all files in the folder
if (candidate.EndsWith(@"\") ||
candidate.EndsWith("/") ||
Directory.Exists(Path.Combine(projectDir, candidate)))
{
return Path.Combine(candidate, "**", "*");
}
// Otherwise, it represents a single file
return candidate;
}
}
}

View file

@ -0,0 +1,45 @@
// 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 Microsoft.Extensions.Internal;
namespace Microsoft.DotNet.ProjectModel.Files
{
public class IncludeEntry : IEquatable<IncludeEntry>
{
public string TargetPath { get; }
public string SourcePath { get; }
public bool IsCustomTarget { get; set; }
public IncludeEntry(string target, string source)
{
TargetPath = target;
SourcePath = source;
}
public override bool Equals(object obj)
{
return Equals((IncludeEntry)obj);
}
public override int GetHashCode()
{
var combiner = HashCodeCombiner.Start();
combiner.Add(TargetPath);
combiner.Add(SourcePath);
return combiner.CombinedHash;
}
public bool Equals(IncludeEntry other)
{
return other != null &&
string.Equals(TargetPath, other.TargetPath, StringComparison.Ordinal) &&
string.Equals(SourcePath, other.SourcePath, StringComparison.Ordinal) &&
IsCustomTarget == other.IsCustomTarget;
}
}
}

View file

@ -0,0 +1,182 @@
// 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;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.Extensions.FileSystemGlobbing;
namespace Microsoft.DotNet.ProjectModel.Files
{
public class IncludeFilesResolver
{
public static IEnumerable<IncludeEntry> GetIncludeFiles(IncludeContext context, string targetBasePath, IList<DiagnosticMessage> diagnostics)
{
return GetIncludeFiles(context, targetBasePath, diagnostics, flatten: false);
}
public static IEnumerable<IncludeEntry> GetIncludeFiles(
IncludeContext context,
string targetBasePath,
IList<DiagnosticMessage> diagnostics,
bool flatten)
{
var sourceBasePath = PathUtility.EnsureTrailingSlash(context.SourceBasePath);
targetBasePath = PathUtility.GetPathWithDirectorySeparator(targetBasePath);
var includeEntries = new HashSet<IncludeEntry>();
// Check for illegal characters in target path
if (string.IsNullOrEmpty(targetBasePath))
{
diagnostics?.Add(new DiagnosticMessage(
ErrorCodes.NU1003,
$"Invalid '{context.Option}' section. The target '{targetBasePath}' is invalid, " +
"targets must either be a file name or a directory suffixed with '/'. " +
"The root directory of the package can be specified by using a single '/' character.",
sourceBasePath,
DiagnosticMessageSeverity.Error));
}
else if (targetBasePath.Split('/').Any(s => s.Equals(".") || s.Equals("..")))
{
diagnostics?.Add(new DiagnosticMessage(
ErrorCodes.NU1004,
$"Invalid '{context.Option}' section. " +
$"The target '{targetBasePath}' contains path-traversal characters ('.' or '..'). " +
"These characters are not permitted in target paths.",
sourceBasePath,
DiagnosticMessageSeverity.Error));
}
else
{
var files = GetIncludeFilesCore(
sourceBasePath,
context.IncludePatterns,
context.ExcludePatterns,
context.IncludeFiles,
context.ExcludeFiles,
context.BuiltInsInclude,
context.BuiltInsExclude);
var isFile = targetBasePath[targetBasePath.Length - 1] != Path.DirectorySeparatorChar;
if (isFile && files.Count() > 1)
{
// It's a file. But the glob matched multiple things
diagnostics?.Add(new DiagnosticMessage(
ErrorCodes.NU1005,
$"Invalid '{ProjectFilesCollection.PackIncludePropertyName}' section. " +
$"The target '{targetBasePath}' refers to a single file, but the corresponding pattern " +
"produces multiple files. To mark the target as a directory, suffix it with '/'.",
sourceBasePath,
DiagnosticMessageSeverity.Error));
}
else if (isFile && files.Any())
{
includeEntries.Add(new IncludeEntry(targetBasePath, files.First()));
}
else
{
targetBasePath = targetBasePath.Substring(0, targetBasePath.Length - 1);
foreach (var file in files)
{
string targetPath = null;
if (flatten)
{
targetPath = Path.Combine(targetBasePath, Path.GetFileName(file));
}
else
{
targetPath = Path.Combine(targetBasePath, PathUtility.GetRelativePath(sourceBasePath, file));
}
includeEntries.Add(new IncludeEntry(targetPath, file));
}
}
}
if (context.Mappings != null)
{
// Finally add all the mappings
foreach (var map in context.Mappings)
{
var targetPath = Path.Combine(targetBasePath, PathUtility.GetPathWithDirectorySeparator(map.Key));
foreach (var file in GetIncludeFiles(map.Value, targetPath, diagnostics, flatten))
{
file.IsCustomTarget = true;
// Prefer named targets over default ones
includeEntries.RemoveWhere(f => string.Equals(f.SourcePath, file.SourcePath));
includeEntries.Add(file);
}
}
}
return includeEntries;
}
private static IEnumerable<string> GetIncludeFilesCore(
string sourceBasePath,
List<string> includePatterns,
List<string> excludePatterns,
List<string> includeFiles,
List<string> excludeFiles,
List<string> builtInsInclude,
List<string> builtInsExclude)
{
var literalIncludedFiles = new List<string>();
if (includeFiles != null)
{
// literal included files are added at the last, but the search happens early
// so as to make the process fail early in case there is missing file. fail early
// helps to avoid unnecessary globing for performance optimization
foreach (var literalRelativePath in includeFiles)
{
var fullPath = Path.GetFullPath(Path.Combine(sourceBasePath, literalRelativePath));
if (!File.Exists(fullPath))
{
throw new InvalidOperationException(string.Format("Can't find file {0}", literalRelativePath));
}
literalIncludedFiles.Add(fullPath);
}
}
// Globbing
var matcher = new Matcher();
if (builtInsInclude != null)
{
matcher.AddIncludePatterns(builtInsInclude);
}
if (includePatterns != null)
{
matcher.AddIncludePatterns(includePatterns);
}
if (builtInsExclude != null)
{
matcher.AddExcludePatterns(builtInsExclude);
}
if (excludePatterns != null)
{
matcher.AddExcludePatterns(excludePatterns);
}
var files = matcher.GetResultsInFullPath(sourceBasePath);
files = files.Concat(literalIncludedFiles).Distinct();
if (files.Any() && excludeFiles != null)
{
var literalExcludedFiles = excludeFiles.Select(file => Path.GetFullPath(Path.Combine(sourceBasePath, file)));
files = files.Except(literalExcludedFiles);
}
return files;
}
}
}

View file

@ -39,11 +39,11 @@ namespace Microsoft.DotNet.ProjectModel.Files
return new string[] { json.Value<string>() };
}
if(json.Type == JTokenType.Array)
if (json.Type == JTokenType.Array)
{
return json.Select(v => v.ToString()).ToArray();
}
return new string[0];
}
}
}
}

View file

@ -1,11 +1,10 @@
// 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 Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectModel.Files
{
@ -27,18 +26,21 @@ namespace Microsoft.DotNet.ProjectModel.Files
JToken propertyNameToken;
if (!rawProject.TryGetValue(propertyName, out propertyNameToken))
{
return CreateCollection(projectDirectory, propertyName, defaultPatterns, literalPath);
return IncludeContext.CreateCollection(
projectDirectory, propertyName, defaultPatterns, literalPath);
}
if (propertyNameToken.Type == JTokenType.String)
{
return CreateCollection(projectDirectory, propertyName, new string[] { propertyNameToken.Value<string>() }, literalPath);
return IncludeContext.CreateCollection(
projectDirectory, propertyName, new string[] { propertyNameToken.Value<string>() }, literalPath);
}
if (propertyNameToken.Type == JTokenType.Array)
{
var valuesInArray = propertyNameToken.Values<string>();
return CreateCollection(projectDirectory, propertyName, valuesInArray.Select(s => s.ToString()), literalPath);
return IncludeContext.CreateCollection(
projectDirectory, propertyName, valuesInArray.Select(s => s.ToString()), literalPath);
}
}
catch (Exception ex)
@ -48,59 +50,5 @@ namespace Microsoft.DotNet.ProjectModel.Files
throw FileFormatException.Create("Value must be either string or array.", rawProject.Value<JToken>(propertyName), projectFilePath);
}
private static IEnumerable<string> CreateCollection(string projectDirectory, string propertyName, IEnumerable<string> patternsStrings, bool literalPath)
{
var patterns = patternsStrings.SelectMany(patternsString => GetSourcesSplit(patternsString))
.Select(patternString => patternString.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar));
foreach (var pattern in patterns)
{
if (Path.IsPathRooted(pattern))
{
throw new InvalidOperationException($"The '{propertyName}' property cannot be a rooted path.");
}
if (literalPath && pattern.Contains('*'))
{
throw new InvalidOperationException($"The '{propertyName}' property cannot contain wildcard characters.");
}
}
return new List<string>(patterns.Select(pattern => FolderToPattern(pattern, projectDirectory)));
}
private static IEnumerable<string> GetSourcesSplit(string sourceDescription)
{
if (string.IsNullOrEmpty(sourceDescription))
{
return Enumerable.Empty<string>();
}
return sourceDescription.Split(PatternSeparator, StringSplitOptions.RemoveEmptyEntries);
}
private static string FolderToPattern(string candidate, string projectDir)
{
// This conversion is needed to support current template
// If it's already a pattern, no change is needed
if (candidate.Contains('*'))
{
return candidate;
}
// If the given string ends with a path separator, or it is an existing directory
// we convert this folder name to a pattern matching all files in the folder
if (candidate.EndsWith(@"\") ||
candidate.EndsWith("/") ||
Directory.Exists(Path.Combine(projectDir, candidate)))
{
return Path.Combine(candidate, "**", "*");
}
// Otherwise, it represents a single file
return candidate;
}
}
}

View file

@ -1,10 +1,8 @@
// 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;
using Newtonsoft.Json.Linq;
namespace Microsoft.DotNet.ProjectModel.Files

View file

@ -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 Microsoft.DotNet.ProjectModel.Files;
namespace Microsoft.DotNet.ProjectModel
{
public class PackOptions
{
public string[] Tags { get; set; }
public string[] Owners { get; set; }
public string ReleaseNotes { get; set; }
public string IconUrl { get; set; }
public string ProjectUrl { get; set; }
public string LicenseUrl { get; set; }
public bool RequireLicenseAcceptance { get; set; }
public string RepositoryType { get; set; }
public string RepositoryUrl { get; set; }
public string Summary { get; set; }
public IncludeContext PackInclude { get; set; }
}
}

View file

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Graph;
using NuGet.Frameworks;
@ -47,16 +46,10 @@ namespace Microsoft.DotNet.ProjectModel
public string Copyright { get; set; }
public string Summary { get; set; }
public string Language { get; set; }
public string ReleaseNotes { get; set; }
public string[] Authors { get; set; }
public string[] Owners { get; set; }
public bool EmbedInteropTypes { get; set; }
public NuGetVersion Version { get; set; }
@ -69,28 +62,24 @@ namespace Microsoft.DotNet.ProjectModel
public string EntryPoint { get; set; }
public string ProjectUrl { get; set; }
public string LicenseUrl { get; set; }
public string IconUrl { get; set; }
public bool RequireLicenseAcceptance { get; set; }
public string[] Tags { get; set; }
public string CompilerName { get; set; }
public string TestRunner { get; set; }
public ProjectFilesCollection Files { get; set; }
public PackOptions PackOptions { get; set; }
public RuntimeOptions RuntimeOptions { get; set; }
public IDictionary<string, string> Commands { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public IDictionary<string, IEnumerable<string>> Scripts { get; } = new Dictionary<string, IEnumerable<string>>(StringComparer.OrdinalIgnoreCase);
public string RawRuntimeOptions { get; set; }
public IncludeContext PublishOptions { get; set; }
public List<DiagnosticMessage> Diagnostics { get; } = new List<DiagnosticMessage>();
public bool IsTestProject => !string.IsNullOrEmpty(TestRunner);
public IEnumerable<TargetFrameworkInformation> GetTargetFrameworks()
@ -134,10 +123,10 @@ namespace Microsoft.DotNet.ProjectModel
public bool HasRuntimeOutput(string configuration)
{
var compilationOptions = GetCompilerOptions(targetFramework: null, configurationName: configuration);
var compilerOptions = GetCompilerOptions(targetFramework: null, configurationName: configuration);
// TODO: Make this opt in via another mechanism
return compilationOptions.EmitEntryPoint.GetValueOrDefault() || IsTestProject;
return compilerOptions.EmitEntryPoint.GetValueOrDefault() || IsTestProject;
}
private CommonCompilerOptions GetCompilerOptions()

View file

@ -337,6 +337,11 @@ namespace Microsoft.DotNet.ProjectModel
}
}
if (Project != null)
{
diagnostics.AddRange(Project.Diagnostics);
}
// Create a library manager
var libraryManager = new LibraryManager(libraries.Values.ToList(), diagnostics, Project?.ProjectFilePath);
@ -567,9 +572,8 @@ namespace Microsoft.DotNet.ProjectModel
private Project ResolveProject(string projectDirectory)
{
// TODO: Handle diagnostics
Project project;
if (ProjectReader.TryGetProject(projectDirectory, out project, diagnostics: null, settings: Settings))
if (ProjectReader.TryGetProject(projectDirectory, out project, settings: Settings))
{
return project;
}

View file

@ -21,7 +21,7 @@ namespace Microsoft.DotNet.ProjectModel
{
foreach (var kvp in _compilerNameToLanguageId)
{
if (kvp.Key == project.CompilerName)
if (kvp.Key == (project._defaultCompilerOptions.CompilerName))
{
return kvp.Value;
}

View file

@ -17,7 +17,7 @@ namespace Microsoft.DotNet.ProjectModel
{
public class ProjectReader
{
public static bool TryGetProject(string path, out Project project, ICollection<DiagnosticMessage> diagnostics = null, ProjectReaderSettings settings = null)
public static bool TryGetProject(string path, out Project project, ProjectReaderSettings settings = null)
{
project = null;
@ -51,7 +51,7 @@ namespace Microsoft.DotNet.ProjectModel
using (var stream = File.OpenRead(projectPath))
{
var reader = new ProjectReader();
project = reader.ReadProject(stream, projectName, projectPath, diagnostics, settings);
project = reader.ReadProject(stream, projectName, projectPath, settings);
}
}
catch (Exception ex)
@ -62,9 +62,7 @@ namespace Microsoft.DotNet.ProjectModel
return true;
}
public static Project GetProject(string projectPath, ProjectReaderSettings settings = null) => GetProject(projectPath, new List<DiagnosticMessage>(), settings);
public static Project GetProject(string projectPath, ICollection<DiagnosticMessage> diagnostics, ProjectReaderSettings settings = null)
public static Project GetProject(string projectPath, ProjectReaderSettings settings = null)
{
if (!projectPath.EndsWith(Project.FileName))
{
@ -75,11 +73,11 @@ namespace Microsoft.DotNet.ProjectModel
using (var stream = new FileStream(projectPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return new ProjectReader().ReadProject(stream, name, projectPath, diagnostics, settings);
return new ProjectReader().ReadProject(stream, name, projectPath, settings);
}
}
public Project ReadProject(Stream stream, string projectName, string projectPath, ICollection<DiagnosticMessage> diagnostics, ProjectReaderSettings settings = null)
public Project ReadProject(Stream stream, string projectName, string projectPath, ProjectReaderSettings settings = null)
{
settings = settings ?? new ProjectReaderSettings();
var project = new Project();
@ -146,25 +144,13 @@ namespace Microsoft.DotNet.ProjectModel
}
project.Description = rawProject.Value<string>("description");
project.Summary = rawProject.Value<string>("summary");
project.Copyright = rawProject.Value<string>("copyright");
project.Title = rawProject.Value<string>("title");
project.EntryPoint = rawProject.Value<string>("entryPoint");
project.ProjectUrl = rawProject.Value<string>("projectUrl");
project.LicenseUrl = rawProject.Value<string>("licenseUrl");
project.IconUrl = rawProject.Value<string>("iconUrl");
project.CompilerName = rawProject.Value<string>("compilerName") ?? "csc";
project.TestRunner = rawProject.Value<string>("testRunner");
project.Authors =
rawProject.Value<JToken>("authors")?.Values<string>().ToArray() ?? EmptyArray<string>.Value;
project.Owners = rawProject.Value<JToken>("owners")?.Values<string>().ToArray() ?? EmptyArray<string>.Value;
project.Tags = rawProject.Value<JToken>("tags")?.Values<string>().ToArray() ?? EmptyArray<string>.Value;
project.Language = rawProject.Value<string>("language");
project.ReleaseNotes = rawProject.Value<string>("releaseNotes");
project.RequireLicenseAcceptance = rawProject.Value<bool>("requireLicenseAcceptance");
// REVIEW: Move this to the dependencies node?
project.EmbedInteropTypes = rawProject.Value<bool>("embedInteropTypes");
@ -215,7 +201,11 @@ namespace Microsoft.DotNet.ProjectModel
}
}
BuildTargetFrameworksAndConfigurations(project, rawProject, diagnostics);
project.PackOptions = GetPackOptions(rawProject, project) ?? new PackOptions();
project.RuntimeOptions = GetRuntimeOptions(rawProject) ?? new RuntimeOptions();
project.PublishOptions = GetPublishInclude(rawProject, project);
BuildTargetFrameworksAndConfigurations(project, rawProject);
PopulateDependencies(
project.ProjectFilePath,
@ -351,11 +341,11 @@ namespace Microsoft.DotNet.ProjectModel
}
}
private void BuildTargetFrameworksAndConfigurations(Project project, JObject projectJsonObject, ICollection<DiagnosticMessage> diagnostics)
private void BuildTargetFrameworksAndConfigurations(Project project, JObject projectJsonObject)
{
// Get the shared compilationOptions
project._defaultCompilerOptions =
GetCompilationOptions(projectJsonObject, project) ?? new CommonCompilerOptions();
GetCompilationOptions(projectJsonObject, project) ?? new CommonCompilerOptions { CompilerName = "csc" };
project._defaultTargetFrameworkConfiguration = new TargetFrameworkInformation
{
@ -423,7 +413,7 @@ namespace Microsoft.DotNet.ProjectModel
if (!success)
{
var lineInfo = (IJsonLineInfo)framework.Value;
diagnostics?.Add(
project.Diagnostics?.Add(
new DiagnosticMessage(
ErrorCodes.NU1008,
$"\"{framework.Key}\" is an unsupported framework.",
@ -518,10 +508,42 @@ namespace Microsoft.DotNet.ProjectModel
private static CommonCompilerOptions GetCompilationOptions(JObject rawObject, Project project)
{
var rawOptions = rawObject.Value<JToken>("compilationOptions") as JObject;
var compilerName = rawObject.Value<string>("compilerName");
if (compilerName != null)
{
var lineInfo = rawObject.Value<IJsonLineInfo>("compilerName");
project.Diagnostics?.Add(
new DiagnosticMessage(
ErrorCodes.DOTNET1016,
$"The 'compilerName' option in the root is deprecated. Use it in 'buildOptions' instead.",
project.ProjectFilePath,
DiagnosticMessageSeverity.Warning,
lineInfo.LineNumber,
lineInfo.LinePosition));
}
var rawOptions = rawObject.Value<JToken>("buildOptions") as JObject;
if (rawOptions == null)
{
return null;
rawOptions = rawObject.Value<JToken>("compilationOptions") as JObject;
if (rawOptions == null)
{
return new CommonCompilerOptions
{
CompilerName = compilerName ?? "csc"
};
}
var lineInfo = (IJsonLineInfo)rawOptions;
project.Diagnostics?.Add(
new DiagnosticMessage(
ErrorCodes.DOTNET1015,
$"The 'compilationOptions' option is deprecated. Use 'buildOptions' instead.",
project.ProjectFilePath,
DiagnosticMessageSeverity.Warning,
lineInfo.LineNumber,
lineInfo.LinePosition));
}
var analyzerOptionsJson = rawOptions.Value<JToken>("analyzerOptions") as JObject;
@ -571,10 +593,148 @@ namespace Microsoft.DotNet.ProjectModel
EmitEntryPoint = rawOptions.Value<bool?>("emitEntryPoint"),
GenerateXmlDocumentation = rawOptions.Value<bool?>("xmlDoc"),
PreserveCompilationContext = rawOptions.Value<bool?>("preserveCompilationContext"),
OutputName = rawOptions.Value<string>("outputName")
OutputName = rawOptions.Value<string>("outputName"),
CompilerName = rawOptions.Value<string>("compilerName") ?? compilerName ?? "csc",
CompileInclude = GetIncludeContext(
project,
rawOptions,
"compile",
defaultBuiltInInclude: ProjectFilesCollection.DefaultCompileBuiltInPatterns,
defaultBuiltInExclude: ProjectFilesCollection.DefaultBuiltInExcludePatterns),
EmbedInclude = GetIncludeContext(
project,
rawOptions,
"embed",
defaultBuiltInInclude: ProjectFilesCollection.DefaultResourcesBuiltInPatterns,
defaultBuiltInExclude: ProjectFilesCollection.DefaultBuiltInExcludePatterns),
CopyToOutputInclude = GetIncludeContext(
project,
rawOptions,
"copyToOutput",
defaultBuiltInInclude: null,
defaultBuiltInExclude: ProjectFilesCollection.DefaultPublishExcludePatterns)
};
}
private static IncludeContext GetIncludeContext(
Project project,
JObject rawOptions,
string option,
string[] defaultBuiltInInclude,
string[] defaultBuiltInExclude)
{
var contextOption = rawOptions.Value<JToken>(option);
if (contextOption != null)
{
return new IncludeContext(
project.ProjectDirectory,
option,
rawOptions,
defaultBuiltInInclude,
defaultBuiltInExclude);
}
return null;
}
private static PackOptions GetPackOptions(JObject rawProject, Project project)
{
var rawPackOptions = rawProject.Value<JToken>("packOptions") as JObject;
// Files to be packed along with the project
IncludeContext packInclude = null;
if (rawPackOptions != null && rawPackOptions.Value<JToken>("files") != null)
{
packInclude = new IncludeContext(
project.ProjectDirectory,
"files",
rawPackOptions,
defaultBuiltInInclude: null,
defaultBuiltInExclude: ProjectFilesCollection.DefaultBuiltInExcludePatterns);
}
var repository = GetPackOptionsValue<JToken>("repository", rawProject, rawPackOptions, project) as JObject;
return new PackOptions
{
ProjectUrl = GetPackOptionsValue<string>("projectUrl", rawProject, rawPackOptions, project),
LicenseUrl = GetPackOptionsValue<string>("licenseUrl", rawProject, rawPackOptions, project),
IconUrl = GetPackOptionsValue<string>("iconUrl", rawProject, rawPackOptions, project),
Owners = GetPackOptionsValue<JToken>("owners", rawProject, rawPackOptions, project)?.Values<string>().ToArray() ?? EmptyArray<string>.Value,
Tags = GetPackOptionsValue<JToken>("tags", rawProject, rawPackOptions, project)?.Values<string>().ToArray() ?? EmptyArray<string>.Value,
ReleaseNotes = GetPackOptionsValue<string>("releaseNotes", rawProject, rawPackOptions, project),
RequireLicenseAcceptance = GetPackOptionsValue<bool>("requireLicenseAcceptance", rawProject, rawPackOptions, project),
Summary = GetPackOptionsValue<string>("summary", rawProject, rawPackOptions, project),
RepositoryType = repository?.Value<string>("type"),
RepositoryUrl = repository?.Value<string>("url"),
PackInclude = packInclude
};
}
private static T GetPackOptionsValue<T>(
string option,
JObject rawProject,
JObject rawPackOptions,
Project project)
{
var rootValue = rawProject.Value<T>(option);
if (rawProject.GetValue(option) != null)
{
var lineInfo = rawProject.Value<IJsonLineInfo>(option);
project.Diagnostics?.Add(
new DiagnosticMessage(
ErrorCodes.DOTNET1016,
$"The '{option}' option in the root is deprecated. Use it in 'packOptions' instead.",
project.ProjectFilePath,
DiagnosticMessageSeverity.Warning,
lineInfo.LineNumber,
lineInfo.LinePosition));
}
if (rawPackOptions != null)
{
var packOptionValue = rawPackOptions.Value<T>(option);
if (packOptionValue != null)
{
return packOptionValue;
}
}
return rootValue;
}
private static RuntimeOptions GetRuntimeOptions(JObject rawProject)
{
var rawRuntimeOptions = rawProject.Value<JToken>("runtimeOptions") as JObject;
if (rawRuntimeOptions == null)
{
return null;
}
return new RuntimeOptions
{
// Value<T>(null) will return default(T) which is false in this case.
GcServer = rawRuntimeOptions.Value<bool>("gcServer"),
GcConcurrent = rawRuntimeOptions.Value<bool>("gcConcurrent")
};
}
private static IncludeContext GetPublishInclude(JObject rawProject, Project project)
{
var rawPublishOptions = rawProject.Value<JToken>("publishOptions");
if (rawPublishOptions != null)
{
return new IncludeContext(
project.ProjectDirectory,
"publishOptions",
rawProject,
defaultBuiltInInclude: null,
defaultBuiltInExclude: ProjectFilesCollection.DefaultPublishExcludePatterns);
}
return null;
}
private static string MakeDefaultTargetFrameworkDefine(NuGetFramework targetFramework)
{
var shortName = targetFramework.GetTwoDigitShortFolderName();

View file

@ -0,0 +1,12 @@
// 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
{
public class RuntimeOptions
{
public bool GcServer { get; set; }
public bool GcConcurrent { get; set; }
}
}

View file

@ -38,9 +38,9 @@ namespace Microsoft.DotNet.ProjectModel
extension = FileNameSuffixes.DotNet.DynamicLib;
}
var compilationOptions = Project.GetCompilerOptions(Framework, Configuration);
var compilerOptions = Project.GetCompilerOptions(Framework, Configuration);
return Path.Combine(BasePath, compilationOptions.OutputName + extension);
return Path.Combine(BasePath, compilerOptions.OutputName + extension);
}
}

View file

@ -284,13 +284,14 @@ namespace Microsoft.DotNet.ProjectModel
if (currentEntry.IsInvalid)
{
Project project;
if (!ProjectReader.TryGetProject(projectDirectory, out project, currentEntry.Diagnostics, _settings))
if (!ProjectReader.TryGetProject(projectDirectory, out project, _settings))
{
currentEntry.Reset();
}
else
{
currentEntry.Model = project;
currentEntry.Diagnostics.AddRange(project.Diagnostics);
currentEntry.FilePath = project.ProjectFilePath;
currentEntry.UpdateLastWriteTimeUtc();
}

View file

@ -54,6 +54,7 @@ namespace Microsoft.DotNet.Tools.Build
var calculator = project.GetOutputPaths(_configuration, _buildBasePath, _outputPath);
var binariesOutputPath = calculator.CompilationOutputPath;
var compilerOptions = project.ProjectFile.GetCompilerOptions(project.TargetFramework, _configuration);
// input: project.json
inputs.Add(project.ProjectFile.ProjectFilePath);
@ -62,7 +63,7 @@ namespace Microsoft.DotNet.Tools.Build
AddLockFile(project, inputs);
// input: source files
inputs.AddRange(CompilerUtil.GetCompilationSources(project));
inputs.AddRange(CompilerUtil.GetCompilationSources(project, compilerOptions));
var allOutputPath = new HashSet<string>(calculator.CompilationFiles.All());
if (isRootProject && project.ProjectFile.HasRuntimeOutput(_configuration))
@ -84,10 +85,10 @@ namespace Microsoft.DotNet.Tools.Build
AddCompilationOptions(project, _configuration, inputs);
// input / output: resources with culture
AddNonCultureResources(project, calculator.IntermediateOutputDirectoryPath, inputs, outputs);
AddNonCultureResources(project, calculator.IntermediateOutputDirectoryPath, inputs, outputs, compilerOptions);
// input / output: resources without culture
AddCultureResources(project, binariesOutputPath, inputs, outputs);
AddCultureResources(project, binariesOutputPath, inputs, outputs, compilerOptions);
return new CompilerIO(inputs, outputs);
}
@ -121,9 +122,24 @@ namespace Microsoft.DotNet.Tools.Build
}
}
private static void AddNonCultureResources(ProjectContext project, string intermediaryOutputPath, List<string> inputs, IList<string> outputs)
private static void AddNonCultureResources(
ProjectContext project,
string intermediaryOutputPath,
List<string> inputs,
IList<string> outputs,
CommonCompilerOptions compilationOptions)
{
foreach (var resourceIO in CompilerUtil.GetNonCultureResources(project.ProjectFile, intermediaryOutputPath))
List<CompilerUtil.NonCultureResgenIO> resources = null;
if (compilationOptions.EmbedInclude == null)
{
resources = CompilerUtil.GetNonCultureResources(project.ProjectFile, intermediaryOutputPath);
}
else
{
resources = CompilerUtil.GetNonCultureResourcesFromIncludeEntries(project.ProjectFile, intermediaryOutputPath, compilationOptions);
}
foreach (var resourceIO in resources)
{
inputs.Add(resourceIO.InputFile);
@ -134,9 +150,24 @@ namespace Microsoft.DotNet.Tools.Build
}
}
private static void AddCultureResources(ProjectContext project, string outputPath, List<string> inputs, List<string> outputs)
private static void AddCultureResources(
ProjectContext project,
string outputPath,
List<string> inputs,
List<string> outputs,
CommonCompilerOptions compilationOptions)
{
foreach (var cultureResourceIO in CompilerUtil.GetCultureResources(project.ProjectFile, outputPath))
List<CompilerUtil.CultureResgenIO> resources = null;
if (compilationOptions.EmbedInclude == null)
{
resources = CompilerUtil.GetCultureResources(project.ProjectFile, outputPath);
}
else
{
resources = CompilerUtil.GetCultureResourcesFromIncludeEntries(project.ProjectFile, outputPath, compilationOptions);
}
foreach (var cultureResourceIO in resources)
{
inputs.AddRange(cultureResourceIO.InputFileToMetadata.Keys);

View file

@ -68,7 +68,8 @@ namespace Microsoft.DotNet.Tools.Build
{
if (project.ProjectFile != null)
{
var projectCompiler = project.ProjectFile.CompilerName;
var compilerOptions = project.ProjectFile.GetCompilerOptions(project.TargetFramework, null);
var projectCompiler = compilerOptions.CompilerName;
if (!KnownCompilers.Any(knownCompiler => knownCompiler.Equals(projectCompiler, StringComparison.Ordinal)))
{

View file

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Tools.Build
@ -67,7 +68,7 @@ namespace Microsoft.DotNet.Tools.Build
}
var context = projectNode.ProjectContext;
if (!context.ProjectFile.Files.SourceFiles.Any())
if (!HasSourceFiles(context))
{
return CompilationResult.IncrementalSkip;
}
@ -82,5 +83,19 @@ namespace Microsoft.DotNet.Tools.Build
return CompilationResult.IncrementalSkip;
}
}
private static bool HasSourceFiles(ProjectContext context)
{
var compilerOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, null);
if (compilerOptions.CompileInclude == null)
{
return context.ProjectFile.Files.SourceFiles.Any();
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
return includeFiles.Any();
}
}
}

View file

@ -46,9 +46,21 @@ namespace Microsoft.DotNet.Tools.Compiler
return success;
}
protected static bool AddNonCultureResources(Project project, List<string> compilerArgs, string intermediateOutputPath)
protected static bool AddNonCultureResources(
Project project,
List<string> compilerArgs,
string intermediateOutputPath,
CommonCompilerOptions compilationOptions)
{
var resgenFiles = CompilerUtil.GetNonCultureResources(project, intermediateOutputPath);
List<CompilerUtil.NonCultureResgenIO> resgenFiles = null;
if (compilationOptions.EmbedInclude == null)
{
resgenFiles = CompilerUtil.GetNonCultureResources(project, intermediateOutputPath);
}
else
{
resgenFiles = CompilerUtil.GetNonCultureResourcesFromIncludeEntries(project, intermediateOutputPath, compilationOptions);
}
foreach (var resgenFile in resgenFiles)
{
@ -80,10 +92,20 @@ namespace Microsoft.DotNet.Tools.Compiler
protected static bool GenerateCultureResourceAssemblies(
Project project,
List<LibraryExport> dependencies,
string outputPath)
string outputPath,
CommonCompilerOptions compilationOptions)
{
var referencePaths = CompilerUtil.GetReferencePathsForCultureResgen(dependencies);
var cultureResgenFiles = CompilerUtil.GetCultureResources(project, outputPath);
List<CompilerUtil.CultureResgenIO> cultureResgenFiles = null;
if (compilationOptions.EmbedInclude == null)
{
cultureResgenFiles = CompilerUtil.GetCultureResources(project, outputPath);
}
else
{
cultureResgenFiles = CompilerUtil.GetCultureResourcesFromIncludeEntries(project, outputPath, compilationOptions);
}
foreach (var resgenFile in cultureResgenFiles)
{

View file

@ -9,6 +9,7 @@ using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Resources;
using Microsoft.DotNet.Tools.Common;
@ -49,11 +50,29 @@ namespace Microsoft.DotNet.Tools.Compiler
// used in incremental compilation
public static List<NonCultureResgenIO> GetNonCultureResources(Project project, string intermediateOutputPath)
{
return
return
(from resourceFile in project.Files.ResourceFiles
let inputFile = resourceFile.Key
let inputFile = resourceFile.Key
where string.IsNullOrEmpty(ResourceUtility.GetResourceCultureName(inputFile))
let metadataName = GetResourceFileMetadataName(project, resourceFile.Key, resourceFile.Value)
let outputFile = ResourceUtility.IsResxFile(inputFile) ? Path.Combine(intermediateOutputPath, metadataName) : null
select new NonCultureResgenIO(inputFile, outputFile, metadataName)
).ToList();
}
// used in incremental compilation
public static List<NonCultureResgenIO> GetNonCultureResourcesFromIncludeEntries(
Project project,
string intermediateOutputPath,
CommonCompilerOptions compilationOptions)
{
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilationOptions.EmbedInclude, "/", diagnostics: null);
return
(from resourceFile in includeFiles
let inputFile = resourceFile.SourcePath
where string.IsNullOrEmpty(ResourceUtility.GetResourceCultureName(inputFile))
let metadataName = GetResourceFileMetadataName(project, resourceFile)
let target = resourceFile.IsCustomTarget ? resourceFile.TargetPath : null
let metadataName = GetResourceFileMetadataName(project, resourceFile.SourcePath, target)
let outputFile = ResourceUtility.IsResxFile(inputFile) ? Path.Combine(intermediateOutputPath, metadataName) : null
select new NonCultureResgenIO(inputFile, outputFile, metadataName)
).ToList();
@ -78,9 +97,29 @@ namespace Microsoft.DotNet.Tools.Compiler
{
return
(from resourceFileGroup in project.Files.ResourceFiles.GroupBy(resourceFile => ResourceUtility.GetResourceCultureName(resourceFile.Key))
let culture = resourceFileGroup.Key
where !string.IsNullOrEmpty(culture)
let inputFileToMetadata = resourceFileGroup.ToDictionary(r => r.Key, r => GetResourceFileMetadataName(project, r.Key, r.Value))
let resourceOutputPath = Path.Combine(outputPath, culture)
let outputFile = Path.Combine(resourceOutputPath, project.Name + ".resources.dll")
select new CultureResgenIO(culture, inputFileToMetadata, outputFile)
).ToList();
}
// used in incremental compilation
public static List<CultureResgenIO> GetCultureResourcesFromIncludeEntries(
Project project,
string outputPath,
CommonCompilerOptions compilationOptions)
{
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilationOptions.EmbedInclude, "/", diagnostics: null);
return
(from resourceFileGroup in includeFiles
.GroupBy(resourceFile => ResourceUtility.GetResourceCultureName(resourceFile.SourcePath))
let culture = resourceFileGroup.Key
where !string.IsNullOrEmpty(culture)
let inputFileToMetadata = resourceFileGroup.ToDictionary(r => r.Key, r => GetResourceFileMetadataName(project, r))
let inputFileToMetadata = resourceFileGroup.ToDictionary(
r => r.SourcePath, r => GetResourceFileMetadataName(project, r.SourcePath, r.IsCustomTarget ? r.TargetPath : null))
let resourceOutputPath = Path.Combine(outputPath, culture)
let outputFile = Path.Combine(resourceOutputPath, project.Name + ".resources.dll")
select new CultureResgenIO(culture, inputFileToMetadata, outputFile)
@ -93,14 +132,14 @@ namespace Microsoft.DotNet.Tools.Compiler
return dependencies.SelectMany(libraryExport => libraryExport.CompilationAssemblies).Select(r => r.ResolvedPath).ToList();
}
public static string GetResourceFileMetadataName(Project project, KeyValuePair<string, string> resourceFile)
public static string GetResourceFileMetadataName(Project project, string resourceFileSource, string resourceFileTarget)
{
string resourceName = null;
string rootNamespace = null;
string root = PathUtility.EnsureTrailingSlash(project.ProjectDirectory);
string resourcePath = resourceFile.Key;
if (string.IsNullOrEmpty(resourceFile.Value))
string resourcePath = resourceFileSource;
if (string.IsNullOrEmpty(resourceFileTarget))
{
// No logical name, so use the file name
resourceName = ResourceUtility.GetResourceName(root, resourcePath);
@ -108,7 +147,7 @@ namespace Microsoft.DotNet.Tools.Compiler
}
else
{
resourceName = ResourceManifestName.EnsureResourceExtension(resourceFile.Value, resourcePath);
resourceName = ResourceManifestName.EnsureResourceExtension(resourceFileTarget, resourcePath);
rootNamespace = null;
}
@ -117,12 +156,23 @@ namespace Microsoft.DotNet.Tools.Compiler
}
// used in incremental compilation
public static IEnumerable<string> GetCompilationSources(ProjectContext project) => project.ProjectFile.Files.SourceFiles;
public static IEnumerable<string> GetCompilationSources(ProjectContext project, CommonCompilerOptions compilerOptions)
{
if (compilerOptions.CompileInclude == null)
{
return project.ProjectFile.Files.SourceFiles;
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
return includeFiles.Select(f => f.SourcePath);
}
//used in incremental precondition checks
public static IEnumerable<string> GetCommandsInvokedByCompile(ProjectContext project)
{
return new List<string> {project.ProjectFile?.CompilerName, "compile"};
var compilerOptions = project.ProjectFile.GetCompilerOptions(project.TargetFramework, configurationName: null);
return new List<string> { compilerOptions.CompilerName, "compile" };
}
}
}

View file

@ -143,15 +143,15 @@ namespace Microsoft.DotNet.Tools.Compiler
compilerArgs.Add($"--resource:\"{depsJsonFile}\",{compilationOptions.OutputName}.deps.json");
}
if (!AddNonCultureResources(context.ProjectFile, compilerArgs, intermediateOutputPath))
if (!AddNonCultureResources(context.ProjectFile, compilerArgs, intermediateOutputPath, compilationOptions))
{
return false;
}
// Add project source files
var sourceFiles = CompilerUtil.GetCompilationSources(context);
var sourceFiles = CompilerUtil.GetCompilationSources(context, compilationOptions);
compilerArgs.AddRange(sourceFiles);
var compilerName = context.ProjectFile.CompilerName;
var compilerName = compilationOptions.CompilerName;
// Write RSP file
var rsp = Path.Combine(intermediateOutputPath, $"dotnet-compile.rsp");
@ -207,7 +207,7 @@ namespace Microsoft.DotNet.Tools.Compiler
if (success)
{
success &= GenerateCultureResourceAssemblies(context.ProjectFile, dependencies, outputPath);
success &= GenerateCultureResourceAssemblies(context.ProjectFile, dependencies, outputPath, compilationOptions);
}
return PrintSummary(diagnostics, sw, success);

View file

@ -1,11 +1,10 @@
// 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.Linq;
using System.Text;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
namespace Microsoft.DotNet.Tools.Pack
{
@ -32,7 +31,7 @@ namespace Microsoft.DotNet.Tools.Pack
public int Execute()
{
if (_project.Files.SourceFiles.Any())
if (HasSourceFiles())
{
var argsBuilder = new List<string>();
argsBuilder.Add("--configuration");
@ -59,5 +58,20 @@ namespace Microsoft.DotNet.Tools.Pack
return 0;
}
private bool HasSourceFiles()
{
var compilerOptions = _project.GetCompilerOptions(
_project.GetTargetFramework(targetFramework: null).FrameworkName, _configuration);
if (compilerOptions.CompileInclude == null)
{
return _project.Files.SourceFiles.Any();
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
return includeFiles.Any();
}
}
}

View file

@ -9,16 +9,15 @@ using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Resources;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.DotNet.Tools.Pack;
using Microsoft.Extensions.FileSystemGlobbing;
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
using NuGet;
using NuGet.Frameworks;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.ProjectModel.Resources;
using Microsoft.DotNet.Tools.Pack;
using PackageBuilder = NuGet.PackageBuilder;
namespace Microsoft.DotNet.Tools.Compiler
@ -42,7 +41,6 @@ namespace Microsoft.DotNet.Tools.Compiler
public bool BuildPackage(IEnumerable<ProjectContext> contexts, List<DiagnosticMessage> packDiagnostics)
{
Reporter.Output.WriteLine($"Producing nuget package \"{GetPackageName()}\" for {Project.Name}");
PackageBuilder = CreatePackageBuilder(Project);
@ -76,15 +74,26 @@ namespace Microsoft.DotNet.Tools.Compiler
var inputFolder = ArtifactPathsCalculator.InputPathForContext(context);
var compilationOptions = Project.GetCompilerOptions(context.TargetFramework, Configuration);
var outputName = compilationOptions.OutputName;
var compilerOptions = Project.GetCompilerOptions(context.TargetFramework, Configuration);
var outputName = compilerOptions.OutputName;
var outputExtension =
context.TargetFramework.IsDesktop() && compilationOptions.EmitEntryPoint.GetValueOrDefault()
context.TargetFramework.IsDesktop() && compilerOptions.EmitEntryPoint.GetValueOrDefault()
? ".exe" : ".dll";
var resourceCultures = context.ProjectFile.Files.ResourceFiles
IEnumerable<string> resourceCultures = null;
if (compilerOptions.EmbedInclude == null)
{
resourceCultures = context.ProjectFile.Files.ResourceFiles
.Select(resourceFile => ResourceUtility.GetResourceCultureName(resourceFile.Key))
.Distinct();
}
else
{
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.EmbedInclude, "/", diagnostics: null);
resourceCultures = includeFiles
.Select(file => ResourceUtility.GetResourceCultureName(file.SourcePath))
.Distinct();
}
foreach (var culture in resourceCultures)
{
@ -112,7 +121,12 @@ namespace Microsoft.DotNet.Tools.Compiler
PackageBuilder.Files.Add(file);
}
if (Project.Files.PackInclude != null && Project.Files.PackInclude.Any())
if (Project.PackOptions.PackInclude != null)
{
var files = IncludeFilesResolver.GetIncludeFiles(Project.PackOptions.PackInclude, "/", diagnostics: packDiagnostics, flatten: true);
PackageBuilder.Files.AddRange(GetPackageFiles(files, packDiagnostics));
}
else if (Project.Files.PackInclude != null && Project.Files.PackInclude.Any())
{
AddPackageFiles(Project.Files.PackInclude, packDiagnostics);
}
@ -237,6 +251,20 @@ namespace Microsoft.DotNet.Tools.Compiler
}
}
private static IEnumerable<PhysicalPackageFile> GetPackageFiles(
IEnumerable<IncludeEntry> includeFiles,
IList<DiagnosticMessage> diagnostics)
{
foreach (var entry in includeFiles)
{
yield return new PhysicalPackageFile()
{
SourcePath = PathUtility.GetPathWithDirectorySeparator(entry.SourcePath),
TargetPath = PathUtility.GetPathWithDirectorySeparator(entry.TargetPath)
};
}
}
protected void TryAddOutputFile(ProjectContext context,
string outputPath,
string filePath)
@ -333,7 +361,7 @@ namespace Microsoft.DotNet.Tools.Compiler
{
var builder = new PackageBuilder();
builder.Authors.AddRange(project.Authors);
builder.Owners.AddRange(project.Owners);
builder.Owners.AddRange(project.PackOptions.Owners);
if (builder.Authors.Count == 0)
{
@ -352,26 +380,26 @@ namespace Microsoft.DotNet.Tools.Compiler
builder.Id = project.Name;
builder.Version = project.Version;
builder.Title = project.Title;
builder.Summary = project.Summary;
builder.Summary = project.PackOptions.Summary;
builder.Copyright = project.Copyright;
builder.RequireLicenseAcceptance = project.RequireLicenseAcceptance;
builder.ReleaseNotes = project.ReleaseNotes;
builder.RequireLicenseAcceptance = project.PackOptions.RequireLicenseAcceptance;
builder.ReleaseNotes = project.PackOptions.ReleaseNotes;
builder.Language = project.Language;
builder.Tags.AddRange(project.Tags);
builder.Tags.AddRange(project.PackOptions.Tags);
if (!string.IsNullOrEmpty(project.IconUrl))
if (!string.IsNullOrEmpty(project.PackOptions.IconUrl))
{
builder.IconUrl = new Uri(project.IconUrl);
builder.IconUrl = new Uri(project.PackOptions.IconUrl);
}
if (!string.IsNullOrEmpty(project.ProjectUrl))
if (!string.IsNullOrEmpty(project.PackOptions.ProjectUrl))
{
builder.ProjectUrl = new Uri(project.ProjectUrl);
builder.ProjectUrl = new Uri(project.PackOptions.ProjectUrl);
}
if (!string.IsNullOrEmpty(project.LicenseUrl))
if (!string.IsNullOrEmpty(project.PackOptions.LicenseUrl))
{
builder.LicenseUrl = new Uri(project.LicenseUrl);
builder.LicenseUrl = new Uri(project.PackOptions.LicenseUrl);
}
return builder;

View file

@ -1,11 +1,12 @@
// 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 Microsoft.DotNet.ProjectModel;
using NuGet;
using System.Collections.Generic;
using System.IO;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.Tools.Pack;
using NuGet;
namespace Microsoft.DotNet.Tools.Compiler
{
@ -34,16 +35,37 @@ namespace Microsoft.DotNet.Tools.Compiler
protected override bool GeneratePackage(string nupkg, List<DiagnosticMessage> packDiagnostics)
{
foreach (var path in Project.Files.SourceFiles)
{
var srcFile = new PhysicalPackageFile
{
SourcePath = path,
TargetPath = Path.Combine("src", Common.PathUtility.GetRelativePath(Project.ProjectDirectory, path))
};
var compilerOptions = Project.GetCompilerOptions(
Project.GetTargetFramework(targetFramework: null).FrameworkName, Configuration);
PackageBuilder.Files.Add(srcFile);
if (compilerOptions.CompileInclude == null)
{
foreach (var path in Project.Files.SourceFiles)
{
var srcFile = new PhysicalPackageFile
{
SourcePath = path,
TargetPath = Path.Combine("src", Common.PathUtility.GetRelativePath(Project.ProjectDirectory, path))
};
PackageBuilder.Files.Add(srcFile);
}
}
else
{
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
foreach (var entry in includeFiles)
{
var srcFile = new PhysicalPackageFile
{
SourcePath = entry.SourcePath,
TargetPath = Path.Combine("src", entry.TargetPath)
};
PackageBuilder.Files.Add(srcFile);
}
}
return base.GeneratePackage(nupkg, packDiagnostics);
}
}

View file

@ -4,10 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.Cli.Compiler.Common;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Server.Helpers;
using Microsoft.DotNet.ProjectModel.Server.Models;
using Microsoft.DotNet.Cli.Compiler.Common;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ProjectModel.Server
@ -37,7 +38,7 @@ namespace Microsoft.DotNet.ProjectModel.Server
.GetAllExports()
.ToDictionary(export => export.Library.Identity.Name);
var allSourceFiles = new List<string>(context.ProjectFile.Files.SourceFiles);
var allSourceFiles = new List<string>(GetSourceFiles(context, configuration));
var allFileReferences = new List<string>();
var allProjectReferences = new List<ProjectReferenceDescription>();
var allDependencies = new Dictionary<string, DependencyDescription>();
@ -74,5 +75,19 @@ namespace Microsoft.DotNet.ProjectModel.Server
return snapshot;
}
private static IEnumerable<string> GetSourceFiles(ProjectContext context, string configuration)
{
var compilerOptions = context.ProjectFile.GetCompilerOptions(context.TargetFramework, configuration);
if (compilerOptions.CompileInclude == null)
{
return context.ProjectFile.Files.SourceFiles;
}
var includeFiles = IncludeFilesResolver.GetIncludeFiles(compilerOptions.CompileInclude, "/", diagnostics: null);
return includeFiles.Select(f => f.SourcePath);
}
}
}

View file

@ -10,6 +10,7 @@ using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Files;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Utilities;
using Microsoft.DotNet.Tools.Common;
@ -179,7 +180,20 @@ namespace Microsoft.DotNet.Tools.Publish
}
var contentFiles = new ContentFiles(context);
contentFiles.StructuredCopyTo(outputPath);
if (context.ProjectFile.PublishOptions != null)
{
var includeEntries = IncludeFilesResolver.GetIncludeFiles(
context.ProjectFile.PublishOptions,
PathUtility.EnsureTrailingSlash(outputPath),
diagnostics: null);
contentFiles.StructuredCopyTo(outputPath, includeEntries);
}
else
{
contentFiles.StructuredCopyTo(outputPath);
}
// Publish a host if this is an application
if (options.EmitEntryPoint.GetValueOrDefault() && !string.IsNullOrEmpty(context.RuntimeIdentifier))
@ -446,51 +460,6 @@ namespace Microsoft.DotNet.Tools.Publish
return contexts.Select(c => Workspace.GetRuntimeContext(c, rids));
}
private static void CopyContents(ProjectContext context, string outputPath)
{
var contentFiles = context.ProjectFile.Files.GetContentFiles();
Copy(contentFiles, context.ProjectDirectory, outputPath);
}
private static void Copy(IEnumerable<string> contentFiles, string sourceDirectory, string targetDirectory)
{
if (contentFiles == null)
{
throw new ArgumentNullException(nameof(contentFiles));
}
sourceDirectory = PathUtility.EnsureTrailingSlash(sourceDirectory);
targetDirectory = PathUtility.EnsureTrailingSlash(targetDirectory);
foreach (var contentFilePath in contentFiles)
{
Reporter.Verbose.WriteLine($"Publishing {contentFilePath.Green().Bold()} ...");
var fileName = Path.GetFileName(contentFilePath);
var targetFilePath = contentFilePath.Replace(sourceDirectory, targetDirectory);
var targetFileParentFolder = Path.GetDirectoryName(targetFilePath);
// Create directory before copying a file
if (!Directory.Exists(targetFileParentFolder))
{
Directory.CreateDirectory(targetFileParentFolder);
}
File.Copy(
contentFilePath,
targetFilePath,
overwrite: true);
// clear read-only bit if set
var fileAttributes = File.GetAttributes(targetFilePath);
if ((fileAttributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
File.SetAttributes(targetFilePath, fileAttributes & ~FileAttributes.ReadOnly);
}
}
}
private static void RunScripts(ProjectContext context, string name, Dictionary<string, string> contextVariables)
{
foreach (var script in context.ProjectFile.Scripts.GetOrEmpty(name))

View file

@ -83,7 +83,6 @@ namespace Microsoft.DotNet.ProjectModel.Tests
stream,
ProjectName,
ProjectFilePath,
new List<DiagnosticMessage>(),
settings);
}
}

View file

@ -0,0 +1,216 @@
// 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;
using System.Linq;
using System.Text;
using FluentAssertions;
using Microsoft.DotNet.ProjectModel.Files;
using Microsoft.DotNet.ProjectModel.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Microsoft.DotNet.ProjectModel.Tests
{
public class GivenThatIWantToCreateIncludeEntriesFromJson
{
private const string ProjectName = "some project name";
private readonly string ProjectFilePath = PathUtility.EnsureTrailingSlash(AppContext.BaseDirectory);
[Fact]
public void PackInclude_is_null_when_it_is_not_set_in_the_ProjectJson()
{
var json = new JObject();
var project = GetProject(json);
project.PackOptions.PackInclude.Should().BeNull();
}
[Fact]
public void It_sets_PackInclude_when_packInclude_is_set_in_the_ProjectJson()
{
const string somePackTarget = "some pack target";
const string somePackValue = "ff/files/file1.txt";
var json = JObject.Parse(string.Format(@"{{
'packOptions': {{
'files': {{
'mappings': {{
'{0}': {{
'includeFiles': '{1}'
}}
}}
}}
}}}}", somePackTarget, somePackValue));
CreateFile(somePackValue);
var project = GetProject(json);
var packInclude = GetIncludeFiles(project.PackOptions.PackInclude, "/").FirstOrDefault();
packInclude.TargetPath.Should().Be(somePackTarget);
packInclude.SourcePath.Should().Contain(PathUtility.GetPathWithDirectorySeparator(somePackValue));
}
[Fact]
public void It_parses_compile_and_includes_files_successfully()
{
var json = JObject.Parse(@"{
'buildOptions': {
'compile': {
'includeFiles': [ 'files/file1.cs', 'files/file2.cs' ],
'exclude': 'files/*ex.cs'
}
}}");
CreateFile("files/file1.cs");
CreateFile("files/file2.cs");
CreateFile("files/file1ex.cs");
CreateFile("files/file2ex.cs");
var project = GetProject(json);
var compileInclude = GetIncludeFiles(project.GetCompilerOptions(null, null).CompileInclude, "/").ToArray();
compileInclude.Should().HaveCount(2);
compileInclude.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/file1.cs") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/file1.cs")));
compileInclude.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/file2.cs") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/file2.cs")));
}
[Fact]
public void It_parses_namedResources_successfully()
{
const string someString = "some string";
const string someResourcePattern = "files/*.resx";
var json = JObject.Parse(string.Format(@"{{
'buildOptions': {{
'embed': {{
'mappings': {{
'{0}': {{
'include': '{1}'
}}
}}
}}
}}}}", someString, someResourcePattern));
CreateFile("files/Resource.resx");
var project = GetProject(json);
var embedInclude = GetIncludeFiles(project.GetCompilerOptions(null, null).EmbedInclude, "/").FirstOrDefault();
embedInclude.TargetPath.Should().Be(someString);
embedInclude.SourcePath.Should().Contain(PathUtility.GetPathWithDirectorySeparator("files/Resource.resx"));
}
[Fact]
public void It_parses_copyToOutput_and_includes_files_successfully()
{
var json = JObject.Parse(@"{
'buildOptions': {
'copyToOutput': {
'include': 'files/*.txt',
'exclude': 'files/p*.txt',
'excludeFiles': 'files/file1ex.txt',
}
}}");
CreateFile("files/file1.txt");
CreateFile("files/file2.txt");
CreateFile("files/file1ex.txt");
var project = GetProject(json);
var copyToOutputInclude = GetIncludeFiles(project.GetCompilerOptions(null, null).CopyToOutputInclude, "/").ToArray();
copyToOutputInclude.Should().HaveCount(2);
copyToOutputInclude.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/file1.txt") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/file1.txt")));
copyToOutputInclude.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/file2.txt") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/file2.txt")));
}
[Fact]
public void It_parses_PublishOptions_and_includes_files_successfully()
{
var json = JObject.Parse(@"{
'publishOptions': {
'include': 'files/p*.txt',
'exclude': 'files/*ex.txt',
'includeFiles': 'files/pfile2ex.txt'
}}");
CreateFile("files/pfile1.txt");
CreateFile("files/pfile1ex.txt");
CreateFile("files/pfile2ex.txt");
var project = GetProject(json);
var publishOptions = GetIncludeFiles(project.PublishOptions, "/").ToArray();
publishOptions.Should().HaveCount(2);
publishOptions.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/pfile1.txt") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/pfile1.txt")));
publishOptions.Should().Contain(
entry => entry.TargetPath == PathUtility.GetPathWithDirectorySeparator("files/pfile2ex.txt") &&
entry.SourcePath.Contains(PathUtility.GetPathWithDirectorySeparator("files/pfile2ex.txt")));
}
private Project GetProject(JObject json, ProjectReaderSettings settings = null)
{
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream, Encoding.UTF8, 256, true))
{
using (var writer = new JsonTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
json.WriteTo(writer);
}
stream.Position = 0;
var projectReader = new ProjectReader();
return projectReader.ReadProject(
stream,
ProjectName,
ProjectFilePath,
settings);
}
}
}
private IEnumerable<IncludeEntry> GetIncludeFiles(IncludeContext context, string targetBasePath)
{
return IncludeFilesResolver.GetIncludeFiles(context, targetBasePath, null);
}
private void CreateFile(string filePath)
{
filePath = Path.Combine(ProjectFilePath, filePath);
var dirName = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dirName))
{
Directory.CreateDirectory(dirName);
}
File.Create(filePath);
}
}
}

View file

@ -22,6 +22,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
private const string ProjectName = "some project name";
private const string SomeLanguageVersion = "some language version";
private const string SomeOutputName = "some output name";
private const string SomeCompilerName = "some compiler name";
private const string SomePlatform = "some platform";
private const string SomeKeyFile = "some key file";
private const string SomeDebugType = "some debug type";
@ -49,6 +50,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
_jsonCompilationOptions.Add("additionalArguments", new JArray(_someAdditionalArguments));
_jsonCompilationOptions.Add("languageVersion", SomeLanguageVersion);
_jsonCompilationOptions.Add("outputName", SomeOutputName);
_jsonCompilationOptions.Add("compilerName", SomeCompilerName);
_jsonCompilationOptions.Add("platform", SomePlatform);
_jsonCompilationOptions.Add("keyFile", SomeKeyFile);
_jsonCompilationOptions.Add("debugType", SomeDebugType);
@ -68,6 +70,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
AdditionalArguments = _someAdditionalArguments,
LanguageVersion = SomeLanguageVersion,
OutputName = SomeOutputName,
CompilerName = SomeCompilerName,
Platform = SomePlatform,
KeyFile = SomeKeyFile,
DebugType = SomeDebugType,
@ -168,18 +171,18 @@ namespace Microsoft.DotNet.ProjectModel.Tests
public void It_leaves_marketing_information_empty_when_it_is_not_set_in_the_ProjectJson()
{
_emptyProject.Description.Should().BeNull();
_emptyProject.Summary.Should().BeNull();
_emptyProject.PackOptions.Summary.Should().BeNull();
_emptyProject.Copyright.Should().BeNull();
_emptyProject.Title.Should().BeNull();
_emptyProject.EntryPoint.Should().BeNull();
_emptyProject.ProjectUrl.Should().BeNull();
_emptyProject.LicenseUrl.Should().BeNull();
_emptyProject.IconUrl.Should().BeNull();
_emptyProject.PackOptions.ProjectUrl.Should().BeNull();
_emptyProject.PackOptions.LicenseUrl.Should().BeNull();
_emptyProject.PackOptions.IconUrl.Should().BeNull();
_emptyProject.Authors.Should().BeEmpty();
_emptyProject.Owners.Should().BeEmpty();
_emptyProject.Tags.Should().BeEmpty();
_emptyProject.PackOptions.Owners.Should().BeEmpty();
_emptyProject.PackOptions.Tags.Should().BeEmpty();
_emptyProject.Language.Should().BeNull();
_emptyProject.ReleaseNotes.Should().BeNull();
_emptyProject.PackOptions.ReleaseNotes.Should().BeNull();
}
[Fact]
@ -216,24 +219,105 @@ namespace Microsoft.DotNet.ProjectModel.Tests
var project = GetProject(json);
project.Description.Should().Be(someDescription);
project.Summary.Should().Be(someSummary);
project.PackOptions.Summary.Should().Be(someSummary);
project.Copyright.Should().Be(someCopyright);
project.Title.Should().Be(someTitle);
project.EntryPoint.Should().Be(someEntryPoint);
project.ProjectUrl.Should().Be(someProjectUrl);
project.LicenseUrl.Should().Be(someLicenseUrl);
project.IconUrl.Should().Be(someIconUrl);
project.PackOptions.ProjectUrl.Should().Be(someProjectUrl);
project.PackOptions.LicenseUrl.Should().Be(someLicenseUrl);
project.PackOptions.IconUrl.Should().Be(someIconUrl);
project.Authors.Should().Contain(authors);
project.Owners.Should().Contain(owners);
project.Tags.Should().Contain(tags);
project.PackOptions.Owners.Should().Contain(owners);
project.PackOptions.Tags.Should().Contain(tags);
project.Language.Should().Be(someLanguage);
project.ReleaseNotes.Should().Be(someReleaseNotes);
project.PackOptions.ReleaseNotes.Should().Be(someReleaseNotes);
}
[Fact]
public void It_sets_the_marketing_information_when_it_is_set_in_the_ProjectJson_PackOptions()
{
const string someDescription = "some description";
const string someSummary = "some summary";
const string someCopyright = "some copyright";
const string someTitle = "some title";
const string someEntryPoint = "some entry point";
const string someProjectUrl = "some project url";
const string someLicenseUrl = "some license url";
const string someIconUrl = "some icon url";
const string someLanguage = "some language";
const string someReleaseNotes = "someReleaseNotes";
var authors = new[] { "some author", "and another author" };
var owners = new[] { "some owner", "a second owner" };
var tags = new[] { "tag1", "tag2" };
var json = new JObject();
var packOptions = new JObject();
json.Add("description", someDescription);
json.Add("copyright", someCopyright);
json.Add("title", someTitle);
json.Add("entryPoint", someEntryPoint);
json.Add("authors", new JArray(authors));
json.Add("language", someLanguage);
packOptions.Add("summary", someSummary);
packOptions.Add("projectUrl", someProjectUrl);
packOptions.Add("licenseUrl", someLicenseUrl);
packOptions.Add("iconUrl", someIconUrl);
packOptions.Add("owners", new JArray(owners));
packOptions.Add("tags", new JArray(tags));
packOptions.Add("releaseNotes", someReleaseNotes);
json.Add("packOptions", packOptions);
var project = GetProject(json);
project.Description.Should().Be(someDescription);
project.PackOptions.Summary.Should().Be(someSummary);
project.Copyright.Should().Be(someCopyright);
project.Title.Should().Be(someTitle);
project.EntryPoint.Should().Be(someEntryPoint);
project.PackOptions.ProjectUrl.Should().Be(someProjectUrl);
project.PackOptions.LicenseUrl.Should().Be(someLicenseUrl);
project.PackOptions.IconUrl.Should().Be(someIconUrl);
project.Authors.Should().Contain(authors);
project.PackOptions.Owners.Should().Contain(owners);
project.PackOptions.Tags.Should().Contain(tags);
project.Language.Should().Be(someLanguage);
project.PackOptions.ReleaseNotes.Should().Be(someReleaseNotes);
}
[Fact]
public void It_warns_when_deprecated_schema_is_used()
{
var json = new JObject();
json.Add("compilerName", "some compiler");
json.Add("compilationOptions", new JObject());
json.Add("projectUrl", "some project url");
var project = GetProject(json);
project.Diagnostics.Should().HaveCount(3);
project.Diagnostics.Should().Contain(m =>
m.ErrorCode == ErrorCodes.DOTNET1015 &&
m.Severity == DiagnosticMessageSeverity.Warning &&
m.Message == "The 'compilationOptions' option is deprecated. Use 'buildOptions' instead.");
project.Diagnostics.Should().Contain(m =>
m.ErrorCode == ErrorCodes.DOTNET1016 &&
m.Severity == DiagnosticMessageSeverity.Warning &&
m.Message == "The 'projectUrl' option in the root is deprecated. Use it in 'packOptions' instead.");
project.Diagnostics.Should().Contain(m =>
m.ErrorCode == ErrorCodes.DOTNET1016 &&
m.Severity == DiagnosticMessageSeverity.Warning &&
m.Message == "The 'compilerName' option in the root is deprecated. Use it in 'buildOptions' instead.");
}
[Fact]
public void It_sets_the_compilerName_to_csc_when_one_is_not_set_in_the_ProjectJson()
{
_emptyProject.CompilerName.Should().Be("csc");
_emptyProject.GetCompilerOptions(targetFramework: null, configurationName: null).CompilerName.Should().Be("csc");
}
[Fact]
@ -244,7 +328,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
json.Add("compilerName", compilerName);
var project = GetProject(json);
project.CompilerName.Should().Be(compilerName);
project.GetCompilerOptions(targetFramework: null, configurationName: null).CompilerName.Should().Be(compilerName);
}
[Fact]
@ -267,7 +351,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
[Fact]
public void It_sets_requireLicenseAcceptance_to_false_when_one_is_not_set_in_the_ProjectJson()
{
_emptyProject.RequireLicenseAcceptance.Should().BeFalse();
_emptyProject.PackOptions.RequireLicenseAcceptance.Should().BeFalse();
}
[Fact]
@ -277,7 +361,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
json.Add("requireLicenseAcceptance", true);
var project = GetProject(json);
project.RequireLicenseAcceptance.Should().BeTrue();
project.PackOptions.RequireLicenseAcceptance.Should().BeTrue();
}
[Fact]
@ -287,7 +371,7 @@ namespace Microsoft.DotNet.ProjectModel.Tests
json.Add("requireLicenseAcceptance", false);
var project = GetProject(json);
project.RequireLicenseAcceptance.Should().BeFalse();
project.PackOptions.RequireLicenseAcceptance.Should().BeFalse();
}
[Fact]
@ -405,7 +489,8 @@ namespace Microsoft.DotNet.ProjectModel.Tests
{
_emptyProject.GetCompilerOptions(null, null).Should().Be(new CommonCompilerOptions
{
OutputName = ProjectName
OutputName = ProjectName,
CompilerName = "csc"
});
}
@ -471,6 +556,17 @@ namespace Microsoft.DotNet.ProjectModel.Tests
project.GetCompilerOptions(null, null).Should().Be(_commonCompilerOptions);
}
[Fact]
public void It_sets_buildOptions_when_it_is_set_in_the_compilationOptions_in_the_ProjectJson()
{
var json = new JObject();
json.Add("buildOptions", _jsonCompilationOptions);
var project = GetProject(json);
project.GetCompilerOptions(null, null).Should().Be(_commonCompilerOptions);
}
[Fact]
public void It_merges_configuration_sections_set_in_the_ProjectJson()
{
@ -874,7 +970,6 @@ namespace Microsoft.DotNet.ProjectModel.Tests
stream,
ProjectName,
ProjectFilePath,
new List<DiagnosticMessage>(),
settings);
}
}

View file

@ -336,7 +336,10 @@ namespace Microsoft.DotNet.ProjectModel.Tests
var rootProject = new Project()
{
Name = "RootProject",
CompilerName = "csc"
_defaultCompilerOptions = new CommonCompilerOptions
{
CompilerName = "csc"
}
};
var rootProjectDescription = new ProjectDescription(

View file

@ -147,6 +147,48 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
result.StdOut.Should().Contain("MyNamespace.Util");
}
[Fact]
public void EmbeddedResourcesAreCopied()
{
var testInstance = TestAssetsManager.CreateTestInstance("EndToEndTestApp")
.WithLockFiles()
.WithBuildArtifacts();
var root = testInstance.TestRoot;
// run compile
var outputDir = Path.Combine(root, "bin");
var testProject = ProjectUtils.GetProjectJson(root, "EndToEndTestApp");
var buildCommand = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework);
var result = buildCommand.ExecuteWithCapturedOutput();
result.Should().Pass();
var objDirInfo = new DirectoryInfo(Path.Combine(root, "obj", "Debug", DefaultFramework));
objDirInfo.Should().HaveFile("EndToEndTestApp.resource1.resources");
objDirInfo.Should().HaveFile("myresource.resources");
}
[Fact]
public void CopyToOutputFilesAreCopied()
{
var testInstance = TestAssetsManager.CreateTestInstance("EndToEndTestApp")
.WithLockFiles()
.WithBuildArtifacts();
var root = testInstance.TestRoot;
// run compile
var outputDir = Path.Combine(root, "bin");
var testProject = ProjectUtils.GetProjectJson(root, "EndToEndTestApp");
var buildCommand = new BuildCommand(testProject, output: outputDir, framework: DefaultFramework);
var result = buildCommand.ExecuteWithCapturedOutput();
result.Should().Pass();
var outputDirInfo = new DirectoryInfo(Path.Combine(outputDir, "copy"));
outputDirInfo.Should().HaveFile("file.txt");
outputDirInfo.Should().NotHaveFile("fileex.txt");
}
[Fact]
public void CanSetOutputAssemblyNameForLibraries()
{

View file

@ -95,6 +95,24 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests
zip.Entries.Should().Contain(e => e.FullName == "lib/netstandard1.5/TestLibraryWithConfiguration.dll");
}
[Fact]
public void HasIncludedFiles()
{
var testInstance = TestAssetsManager.CreateTestInstance("EndToEndTestApp")
.WithLockFiles()
.WithBuildArtifacts();
var cmd = new PackCommand(Path.Combine(testInstance.TestRoot, Project.FileName));
cmd.Execute().Should().Pass();
var outputPackage = Path.Combine(testInstance.TestRoot, "bin", "Debug", "EndToEndTestApp.1.0.0.nupkg");
File.Exists(outputPackage).Should().BeTrue(outputPackage);
var zip = ZipFile.Open(outputPackage, ZipArchiveMode.Read);
zip.Entries.Should().Contain(e => e.FullName == "pack1.txt");
zip.Entries.Should().Contain(e => e.FullName == "newpath/pack2.txt");
}
[Fact]
public void PackAddsCorrectFilesForProjectsWithOutputNameSpecified()
{

View file

@ -116,6 +116,21 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
publishCommand.GetOutputDirectory().Should().HaveFile("testcontentfile.txt");
}
[Fact]
public void ProjectWithPublishOptionsTest()
{
var instance = TestAssetsManager.CreateTestInstance("EndToEndTestApp")
.WithLockFiles()
.WithBuildArtifacts();
var testProject = _getProjectJson(instance.TestRoot, "EndToEndTestApp");
var publishCommand = new PublishCommand(testProject);
publishCommand.Execute().Should().Pass();
publishCommand.GetOutputDirectory().Should().HaveFile("testpublishfile.txt");
}
[Fact]
public void FailWhenNoRestoreTest()
{