Improve resource file support (#2511)
* Add satellite assemblies to deps file with locale data * Publish satellite assemblies to output during publish * Copy satellite assemblies from project-to-project dependencies on build and publish
This commit is contained in:
parent
777e75f0a9
commit
852446e859
22 changed files with 519 additions and 15 deletions
12
TestAssets/TestProjects/ResourcesTests/TestApp/Program.cs
Normal file
12
TestAssets/TestProjects/ResourcesTests/TestApp/Program.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ConsoleApplication
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
TestAssets/TestProjects/ResourcesTests/TestApp/project.json
Normal file
19
TestAssets/TestProjects/ResourcesTests/TestApp/project.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"compilationOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Data.OData": "5.6.4",
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"type": "platform",
|
||||||
|
"version": "1.0.0-rc2-24008"
|
||||||
|
},
|
||||||
|
"TestLibraryWithResources": { "target": "project" }
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.0": {
|
||||||
|
"imports": [ "portable-net45+win8" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace TestProjectWithCultureSpecificResource
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var rm = new ResourceManager(
|
||||||
|
"TestProjectWithCultureSpecificResource.Strings",
|
||||||
|
typeof(Program).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
Console.WriteLine(rm.GetString("hello"));
|
||||||
|
Console.WriteLine(rm.GetString("hello", new CultureInfo("fr")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="hello" xml:space="preserve">
|
||||||
|
<value>Bonjour!</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="hello" xml:space="preserve">
|
||||||
|
<value>Hello World!</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "1.0.0-*",
|
||||||
|
"dependencies": {
|
||||||
|
"NETStandard.Library": "1.5.0-rc2-24008",
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netstandard1.5": {
|
||||||
|
"imports": "dnxcore50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
"emitEntryPoint": true
|
"emitEntryPoint": true
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"NETStandard.Library": "1.5.0-rc2-24015"
|
"NETStandard.Library": "1.5.0-rc2-24008"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netstandardapp1.5": {
|
"netstandardapp1.5": {
|
||||||
|
|
|
@ -358,12 +358,9 @@ namespace Microsoft.DotNet.Cli.Build
|
||||||
Utils.DeleteDirectory(SharedFrameworkNameAndVersionRoot);
|
Utils.DeleteDirectory(SharedFrameworkNameAndVersionRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
string publishFramework = "dnxcore50"; // Temporary, use "netcoreapp" when we update nuget.
|
|
||||||
|
|
||||||
dotnetCli.Publish(
|
dotnetCli.Publish(
|
||||||
"--output", SharedFrameworkNameAndVersionRoot,
|
"--output", SharedFrameworkNameAndVersionRoot,
|
||||||
"-r", sharedFrameworkRid,
|
"-r", sharedFrameworkRid,
|
||||||
"-f", publishFramework,
|
|
||||||
SharedFrameworkSourceRoot).Execute().EnsureSuccessful();
|
SharedFrameworkSourceRoot).Execute().EnsureSuccessful();
|
||||||
|
|
||||||
// Clean up artifacts that dotnet-publish generates which we don't need
|
// Clean up artifacts that dotnet-publish generates which we don't need
|
||||||
|
|
|
@ -97,6 +97,16 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
|
||||||
{
|
{
|
||||||
libraryExport.RuntimeAssemblyGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
libraryExport.RuntimeAssemblyGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
||||||
libraryExport.NativeLibraryGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
libraryExport.NativeLibraryGroups.GetDefaultAssets().CopyTo(_runtimeOutputPath);
|
||||||
|
|
||||||
|
foreach(var group in libraryExport.ResourceAssemblies.GroupBy(r => r.Locale))
|
||||||
|
{
|
||||||
|
var localeSpecificDir = Path.Combine(_runtimeOutputPath, group.Key);
|
||||||
|
if(!Directory.Exists(localeSpecificDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(localeSpecificDir);
|
||||||
|
}
|
||||||
|
group.Select(r => r.Asset).CopyTo(localeSpecificDir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
|
||||||
var builder = LibraryExportBuilder.Create(library);
|
var builder = LibraryExportBuilder.Create(library);
|
||||||
builder.AddNativeLibraryGroup(new LibraryAssetGroup(PopulateAssets(library, library.NativeLibraries)));
|
builder.AddNativeLibraryGroup(new LibraryAssetGroup(PopulateAssets(library, library.NativeLibraries)));
|
||||||
builder.AddRuntimeAssemblyGroup(new LibraryAssetGroup(PopulateAssets(library, library.RuntimeAssemblies)));
|
builder.AddRuntimeAssemblyGroup(new LibraryAssetGroup(PopulateAssets(library, library.RuntimeAssemblies)));
|
||||||
|
builder.WithResourceAssemblies(PopulateResources(library, library.ResourceAssemblies));
|
||||||
builder.WithCompilationAssemblies(PopulateAssets(library, library.CompileTimeAssemblies));
|
builder.WithCompilationAssemblies(PopulateAssets(library, library.CompileTimeAssemblies));
|
||||||
|
|
||||||
if (library.Identity.Type.Equals(LibraryType.Package))
|
if (library.Identity.Type.Equals(LibraryType.Package))
|
||||||
|
@ -322,6 +323,10 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
|
||||||
builder.AddRuntimeAssemblyGroup(new LibraryAssetGroup(new[] { compilationAssemblyAsset }));
|
builder.AddRuntimeAssemblyGroup(new LibraryAssetGroup(new[] { compilationAssemblyAsset }));
|
||||||
builder.WithRuntimeAssets(CollectAssets(outputPaths.CompilationFiles));
|
builder.WithRuntimeAssets(CollectAssets(outputPaths.CompilationFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.WithResourceAssemblies(outputPaths.CompilationFiles.Resources().Select(r => new LibraryResourceAssembly(
|
||||||
|
LibraryAsset.CreateFromAbsolutePath(outputPaths.CompilationFiles.BasePath, r.Path),
|
||||||
|
r.Locale)));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.WithSourceReferences(project.Project.Files.SharedFiles.Select(f =>
|
builder.WithSourceReferences(project.Project.Files.SharedFiles.Select(f =>
|
||||||
|
@ -334,7 +339,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
|
||||||
private IEnumerable<LibraryAsset> CollectAssets(CompilationOutputFiles files)
|
private IEnumerable<LibraryAsset> CollectAssets(CompilationOutputFiles files)
|
||||||
{
|
{
|
||||||
var assemblyPath = files.Assembly;
|
var assemblyPath = files.Assembly;
|
||||||
foreach (var path in files.All())
|
foreach (var path in files.All().Except(files.Resources().Select(r => r.Path)))
|
||||||
{
|
{
|
||||||
if (string.Equals(assemblyPath, path))
|
if (string.Equals(assemblyPath, path))
|
||||||
{
|
{
|
||||||
|
@ -458,6 +463,21 @@ namespace Microsoft.DotNet.ProjectModel.Compilation
|
||||||
return analyzerRefs;
|
return analyzerRefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<LibraryResourceAssembly> PopulateResources(TargetLibraryWithAssets library, IEnumerable<LockFileItem> section)
|
||||||
|
{
|
||||||
|
foreach (var assemblyPath in section.Where(a => !PackageDependencyProvider.IsPlaceholderFile(a.Path)))
|
||||||
|
{
|
||||||
|
string locale;
|
||||||
|
if(!assemblyPath.Properties.TryGetValue(Constants.LocaleLockFilePropertyName, out locale))
|
||||||
|
{
|
||||||
|
locale = null;
|
||||||
|
}
|
||||||
|
yield return new LibraryResourceAssembly(
|
||||||
|
LibraryAsset.CreateFromRelativePath(library.Path, assemblyPath.Path),
|
||||||
|
locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<LibraryAsset> PopulateAssets(TargetLibraryWithAssets library, IEnumerable<LockFileItem> section)
|
private IEnumerable<LibraryAsset> PopulateAssets(TargetLibraryWithAssets library, IEnumerable<LockFileItem> section)
|
||||||
{
|
{
|
||||||
foreach (var assemblyPath in section.Where(a => !PackageDependencyProvider.IsPlaceholderFile(a.Path)))
|
foreach (var assemblyPath in section.Where(a => !PackageDependencyProvider.IsPlaceholderFile(a.Path)))
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Microsoft.DotNet.ProjectModel
|
||||||
|
|
||||||
public string OutputExtension { get; }
|
public string OutputExtension { get; }
|
||||||
|
|
||||||
public virtual IEnumerable<string> Resources()
|
public virtual IEnumerable<ResourceFile> Resources()
|
||||||
{
|
{
|
||||||
var resourceNames = Project.Files.ResourceFiles
|
var resourceNames = Project.Files.ResourceFiles
|
||||||
.Select(f => ResourceUtility.GetResourceCultureName(f.Key))
|
.Select(f => ResourceUtility.GetResourceCultureName(f.Key))
|
||||||
|
@ -65,7 +65,7 @@ namespace Microsoft.DotNet.ProjectModel
|
||||||
|
|
||||||
foreach (var resourceName in resourceNames)
|
foreach (var resourceName in resourceNames)
|
||||||
{
|
{
|
||||||
yield return Path.Combine(BasePath, resourceName, Project.Name + ".resources" + FileNameSuffixes.DotNet.DynamicLib);
|
yield return new ResourceFile(Path.Combine(BasePath, resourceName, Project.Name + ".resources" + FileNameSuffixes.DotNet.DynamicLib), resourceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ namespace Microsoft.DotNet.ProjectModel
|
||||||
}
|
}
|
||||||
foreach (var resource in Resources())
|
foreach (var resource in Resources())
|
||||||
{
|
{
|
||||||
yield return resource;
|
yield return resource.Path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace Microsoft.DotNet.ProjectModel
|
||||||
public static readonly string DefaultOutputDirectory = "bin";
|
public static readonly string DefaultOutputDirectory = "bin";
|
||||||
public static readonly string DefaultConfiguration = "Debug";
|
public static readonly string DefaultConfiguration = "Debug";
|
||||||
|
|
||||||
|
public static readonly string LocaleLockFilePropertyName = "locale";
|
||||||
|
|
||||||
public static readonly Version Version50 = new Version(5, 0);
|
public static readonly Version Version50 = new Version(5, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,24 @@ namespace Microsoft.DotNet.ProjectModel.Graph
|
||||||
{
|
{
|
||||||
public class LockFileItem
|
public class LockFileItem
|
||||||
{
|
{
|
||||||
|
public LockFileItem()
|
||||||
|
{
|
||||||
|
Properties = new Dictionary<string, string>();;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockFileItem(string path) : this()
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockFileItem(string path, IDictionary<string, string> properties) : this(path)
|
||||||
|
{
|
||||||
|
Properties = new Dictionary<string, string>(properties);
|
||||||
|
}
|
||||||
|
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, string> Properties { get; } = new Dictionary<string, string>();
|
public IDictionary<string, string> Properties { get; }
|
||||||
|
|
||||||
public static implicit operator string (LockFileItem item) => item.Path;
|
public static implicit operator string (LockFileItem item) => item.Path;
|
||||||
|
|
||||||
|
|
19
src/Microsoft.DotNet.ProjectModel/ResourceFile.cs
Normal file
19
src/Microsoft.DotNet.ProjectModel/ResourceFile.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.DotNet.ProjectModel
|
||||||
|
{
|
||||||
|
public class ResourceFile
|
||||||
|
{
|
||||||
|
public string Path { get; }
|
||||||
|
public string Locale { get; }
|
||||||
|
|
||||||
|
public ResourceFile(string path, string locale)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
Locale = locale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,6 +147,16 @@ namespace Microsoft.DotNet.Tools.Publish
|
||||||
|
|
||||||
var runtimeAssetsToCopy = export.RuntimeAssets.Where(a => ShouldCopyExportRuntimeAsset(context, buildOutputPaths, export, a));
|
var runtimeAssetsToCopy = export.RuntimeAssets.Where(a => ShouldCopyExportRuntimeAsset(context, buildOutputPaths, export, a));
|
||||||
runtimeAssetsToCopy.StructuredCopyTo(outputPath, outputPaths.IntermediateOutputDirectoryPath);
|
runtimeAssetsToCopy.StructuredCopyTo(outputPath, outputPaths.IntermediateOutputDirectoryPath);
|
||||||
|
|
||||||
|
foreach(var resourceAsset in export.ResourceAssemblies)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(outputPath, resourceAsset.Locale);
|
||||||
|
if(!Directory.Exists(dir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
File.Copy(resourceAsset.Asset.ResolvedPath, Path.Combine(dir, resourceAsset.Asset.FileName), overwrite: true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.ProjectFile.HasRuntimeOutput(configuration) && !context.TargetFramework.IsDesktop())
|
if (context.ProjectFile.HasRuntimeOutput(configuration) && !context.TargetFramework.IsDesktop())
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"$(RID)": {}
|
"$(RID)": {}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"dnxcore50": {
|
"netcoreapp1.0": {
|
||||||
"imports": [
|
"imports": [
|
||||||
"portable-net45+win8"
|
"portable-net45+win8"
|
||||||
]
|
]
|
||||||
|
|
|
@ -126,6 +126,34 @@ namespace Microsoft.DotNet.ProjectModel.Tests
|
||||||
runtimeAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "lib/Something.OSX.dll"));
|
runtimeAsset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "lib/Something.OSX.dll"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExportsPackageResourceAssemblies()
|
||||||
|
{
|
||||||
|
var description = CreateDescription(
|
||||||
|
new LockFileTargetLibrary()
|
||||||
|
{
|
||||||
|
ResourceAssemblies = new List<LockFileItem>()
|
||||||
|
{
|
||||||
|
new LockFileItem("resources/en-US/Res.dll", new Dictionary<string, string>() { { "locale", "en-US"} }),
|
||||||
|
new LockFileItem("resources/ru-RU/Res.dll", new Dictionary<string, string>() { { "locale", "ru-RU" } }),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = ExportSingle(description);
|
||||||
|
result.ResourceAssemblies.Should().HaveCount(2);
|
||||||
|
var asset = result.ResourceAssemblies.Should().Contain(g => g.Locale == "en-US").Subject.Asset;
|
||||||
|
asset.Name.Should().Be("Res");
|
||||||
|
asset.Transform.Should().BeNull();
|
||||||
|
asset.RelativePath.Should().Be("resources/en-US/Res.dll");
|
||||||
|
asset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "resources/en-US/Res.dll"));
|
||||||
|
|
||||||
|
asset = result.ResourceAssemblies.Should().Contain(g => g.Locale == "ru-RU").Subject.Asset;
|
||||||
|
asset.Name.Should().Be("Res");
|
||||||
|
asset.Transform.Should().BeNull();
|
||||||
|
asset.RelativePath.Should().Be("resources/ru-RU/Res.dll");
|
||||||
|
asset.ResolvedPath.Should().Be(Path.Combine(PackagePath, "resources/ru-RU/Res.dll"));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ExportsSources()
|
public void ExportsSources()
|
||||||
{
|
{
|
||||||
|
|
|
@ -252,6 +252,24 @@ namespace Microsoft.Extensions.DependencyModel.Tests
|
||||||
asm.Assemblies.Should().OnlyContain(a => a == "System.Collections.dll");
|
asm.Assemblies.Should().OnlyContain(a => a == "System.Collections.dll");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FillsResources()
|
||||||
|
{
|
||||||
|
var context = Build(runtimeExports: new[]
|
||||||
|
{
|
||||||
|
Export(PackageDescription("Pack.Age", version: new NuGetVersion(1, 2, 3)),
|
||||||
|
resourceAssemblies: new []
|
||||||
|
{
|
||||||
|
new LibraryResourceAssembly(new LibraryAsset("Dll", "resources/en-US/Pack.Age.dll", ""), "en-US")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
context.RuntimeLibraries.Should().HaveCount(1);
|
||||||
|
|
||||||
|
var lib = context.RuntimeLibraries.Should().Contain(l => l.Name == "Pack.Age").Subject;
|
||||||
|
lib.ResourceAssemblies.Should().OnlyContain(l => l.Locale == "en-US" && l.Path == "resources/en-US/Pack.Age.dll");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ReferenceAssembliesPathRelativeToDefaultRoot()
|
public void ReferenceAssembliesPathRelativeToDefaultRoot()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
// 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.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -8,6 +9,7 @@ using FluentAssertions;
|
||||||
using Microsoft.DotNet.ProjectModel;
|
using Microsoft.DotNet.ProjectModel;
|
||||||
using Microsoft.DotNet.Tools.Test.Utilities;
|
using Microsoft.DotNet.Tools.Test.Utilities;
|
||||||
using Microsoft.Extensions.PlatformAbstractions;
|
using Microsoft.Extensions.PlatformAbstractions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using NuGet.Frameworks;
|
using NuGet.Frameworks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -212,6 +214,41 @@ namespace Microsoft.DotNet.Tools.Builder.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PackageReferenceWithResourcesTest()
|
||||||
|
{
|
||||||
|
var testInstance = TestAssetsManager.CreateTestInstance("ResourcesTests")
|
||||||
|
.WithLockFiles();
|
||||||
|
|
||||||
|
var projectRoot = Path.Combine(testInstance.TestRoot, "TestApp");
|
||||||
|
|
||||||
|
var cmd = new BuildCommand(projectRoot);
|
||||||
|
var result = cmd.Execute();
|
||||||
|
result.Should().Pass();
|
||||||
|
|
||||||
|
var outputDir = new DirectoryInfo(Path.Combine(projectRoot, "bin", "Debug", "netcoreapp1.0"));
|
||||||
|
|
||||||
|
outputDir.Should().HaveFile("TestLibraryWithResources.dll");
|
||||||
|
outputDir.Sub("fr").Should().HaveFile("TestLibraryWithResources.resources.dll");
|
||||||
|
|
||||||
|
var depsJson = JObject.Parse(File.ReadAllText(Path.Combine(outputDir.FullName, $"{Path.GetFileNameWithoutExtension(cmd.GetOutputExecutableName())}.deps.json")));
|
||||||
|
|
||||||
|
foreach (var library in new[] { Tuple.Create("Microsoft.Data.OData", "5.6.4"), Tuple.Create("TestLibraryWithResources", "1.0.0") })
|
||||||
|
{
|
||||||
|
var resources = depsJson["targets"][".NETCoreApp,Version=v1.0"][library.Item1 + "/" + library.Item2]["resources"];
|
||||||
|
|
||||||
|
resources.Should().NotBeNull();
|
||||||
|
|
||||||
|
foreach (var item in resources.Children<JProperty>())
|
||||||
|
{
|
||||||
|
var locale = item.Value["locale"];
|
||||||
|
locale.Should().NotBeNull();
|
||||||
|
|
||||||
|
item.Name.Should().EndWith($"{locale}/{library.Item1}.resources.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ResourceTest()
|
public void ResourceTest()
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,5 +42,45 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
|
||||||
.Should()
|
.Should()
|
||||||
.HaveFile("config.xml");
|
.HaveFile("config.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PublishTestAppWithReferencesToResources()
|
||||||
|
{
|
||||||
|
var testInstance = TestAssetsManager.CreateTestInstance("ResourcesTests")
|
||||||
|
.WithLockFiles();
|
||||||
|
|
||||||
|
var projectRoot = Path.Combine(testInstance.TestRoot, "TestApp");
|
||||||
|
|
||||||
|
var publishCommand = new PublishCommand(projectRoot);
|
||||||
|
var publishResult = publishCommand.Execute();
|
||||||
|
|
||||||
|
publishResult.Should().Pass();
|
||||||
|
|
||||||
|
var publishDir = publishCommand.GetOutputDirectory(portable: true);
|
||||||
|
|
||||||
|
publishDir.Should().HaveFiles(new[]
|
||||||
|
{
|
||||||
|
"TestApp.dll",
|
||||||
|
"TestApp.deps.json"
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var culture in new[] { "de", "es", "fr", "it", "ja", "ko", "ru", "zh-Hans", "zh-Hant" })
|
||||||
|
{
|
||||||
|
var cultureDir = publishDir.Sub(culture);
|
||||||
|
|
||||||
|
// Provided by packages
|
||||||
|
cultureDir.Should().HaveFiles(new[] {
|
||||||
|
"Microsoft.Data.Edm.resources.dll",
|
||||||
|
"Microsoft.Data.OData.resources.dll",
|
||||||
|
"System.Spatial.resources.dll"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for the project-to-project one
|
||||||
|
if (culture == "fr")
|
||||||
|
{
|
||||||
|
cultureDir.Should().HaveFile("TestLibraryWithResources.resources.dll");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue