using System;
using System.IO;
+using System.Text;
using Microsoft.Build.Framework;
namespace Microsoft.Build.Tasks {
public CreateCSharpManifestResourceName ()
{
}
+
+ protected override bool IsSourceFile (string fileName)
+ {
+ return Path.GetExtension (fileName).ToLower () == ".cs";
+ }
- [MonoTODO]
protected override string CreateManifestName (string fileName,
string linkFileName,
string rootNamespace,
string dependentUponFileName,
Stream binaryStream)
{
- throw new NotImplementedException ();
+ if (String.IsNullOrEmpty (dependentUponFileName) || binaryStream == null)
+ return GetResourceIdFromFileName (fileName, rootNamespace);
+
+ string ns = null;
+ string classname = null;
+
+ using (StreamReader rdr = new StreamReader (binaryStream)) {
+ int numopen = 0;
+ while (true) {
+ string tok = GetNextToken (rdr);
+ if (tok == null)
+ break;
+
+ if (tok == "@") {
+ //Handle @namespace, @class
+ GetNextToken (rdr);
+ continue;
+ }
+
+ if (String.Compare (tok, "namespace", false) == 0)
+ ns = GetNextToken (rdr);
+
+ if (tok == "{")
+ numopen ++;
+
+ if (tok == "}") {
+ numopen --;
+ if (numopen == 0)
+ ns = String.Empty;
+ }
+
+ if (tok == "class") {
+ classname = GetNextToken (rdr);
+ break;
+ }
+ }
+
+ if (classname == null)
+ return GetResourceIdFromFileName (fileName, rootNamespace);
+
+ string culture, extn, only_filename;
+ if (AssignCulture.TrySplitResourceName (fileName, out only_filename, out culture, out extn))
+ extn = "." + culture;
+ else
+ extn = String.Empty;
+
+ if (ns == null)
+ return classname + extn;
+ else
+ return ns + '.' + classname + extn;
+ }
}
-
- [MonoTODO]
- protected override bool IsSourceFile (string fileName)
+
+ // No dependent file
+ static string GetResourceIdFromFileName (string fileName, string rootNamespace)
{
- throw new NotImplementedException ();
+ string culture = null;
+ if (String.Compare (Path.GetExtension (fileName), ".resx", true) == 0) {
+ fileName = Path.ChangeExtension (fileName, null);
+ } else {
+ string only_filename, extn;
+ if (AssignCulture.TrySplitResourceName (fileName, out only_filename, out culture, out extn)) {
+ //remove the culture from fileName
+ //foo.it.bmp -> foo.bmp
+ fileName = only_filename + "." + extn;
+ }
+ }
+
+ //FIXME: path char!
+ string rname = fileName.Replace ('/', '.').Replace ('\\', '.');
+
+ if (!String.IsNullOrEmpty (rootNamespace))
+ rname = rootNamespace + "." + rname;
+ if (culture == null)
+ return rname;
+ else
+ //FIXME: Why??!! Tests show that this is required!
+ return culture + "\\" + rname;
}
+
+ /* Special parser for C# files
+ * Assumes that the file is compilable
+ * skips comments,
+ * skips strings "foo",
+ * skips anything after a # , eg. #region, #if
+ * Won't handle #if false etc kinda blocks*/
+ static string GetNextToken (StreamReader sr)
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ while (true) {
+ int c = sr.Peek ();
+ if (c == -1)
+ return null;
+
+ if (c == '\r' || c == '\n') {
+ sr.ReadLine ();
+ if (sb.Length > 0)
+ break;
+
+ continue;
+ }
+
+ if (c == '/') {
+ sr.Read ();
+
+ if (sr.Peek () == '*') {
+ /* multi-line comment */
+ sr.Read ();
+
+ while (true) {
+ int n = sr.Read ();
+ if (n == -1)
+ break;
+ if (n != '*')
+ continue;
+
+ if (sr.Peek () == '/') {
+ /* End of multi-line comment */
+ if (sb.Length > 0) {
+ sr.Read ();
+ return sb.ToString ();
+ }
+ break;
+ }
+ }
+ } else if (sr.Peek () == '/') {
+ //Single line comment, skip the rest of the line
+ sr.ReadLine ();
+ continue;
+ }
+ } else if (c == '"') {
+ /* String "foo" */
+ sr.Read ();
+ while (true) {
+ int n = sr.Peek ();
+ if (n == '\r' || n == '\n' || n == -1)
+ throw new Exception ("String literal not closed");
+
+ if (n == '"') {
+ /* end of string */
+ if (sb.Length > 0) {
+ sr.Read ();
+ return sb.ToString ();
+ }
+
+ break;
+ }
+ sr.Read ();
+ }
+ } else if (c == '#') {
+ //skip rest of the line
+ sr.ReadLine ();
+ } else {
+ if (Char.IsLetterOrDigit ((char) c) || c == '_' || c == '.') {
+ sb.Append ((char) c);
+ } else {
+ if (sb.Length > 0)
+ break;
+
+ if (c != ' ' && c != '\t') {
+ sr.Read ();
+ return ((char) c).ToString ();
+ }
+ }
+ }
+
+ sr.Read ();
+ }
+
+ return sb.ToString ();
+ }
+
}
}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using NUnit.Framework;
+using Microsoft.Build.BuildEngine;
+
+namespace MonoTests.Microsoft.Build.Tasks
+{
+ [TestFixture]
+ public class CreateCSharpManifestResourceNameTest
+ {
+
+ string [,] resx_no_culture_files, resx_with_culture_files;
+ string [,] non_resx_no_culture_files, non_resx_with_culture_files;
+
+ public CreateCSharpManifestResourceNameTest ()
+ {
+ resx_no_culture_files = new string [,] {
+ // With dependent file
+ { "foo.resx", null, "Test\\resources\\Sample.cs" },
+ { "foo.resx", "RandomName", "Test\\resources\\Sample.cs" },
+
+ { "Test/resources/foo.resx", null, "Sample.cs" },
+ { "Test/resources/foo.resx", "RandomName", "Sample.cs" },
+
+ // W/o dependent file
+ { "foo.resx", null, null },
+ { "foo.resx", "RandomName", null },
+
+ { "Test/resources/foo.resx", null, null },
+ { "Test/resources/foo.resx", "RandomName", null },
+ };
+
+ resx_with_culture_files = new string [,] {
+ // With dependent file
+ { "foo.de.resx", null, "Test\\resources\\Sample.cs" },
+ { "foo.de.resx", "RandomName", "Test\\resources\\Sample.cs" },
+
+ { "Test/resources/foo.de.resx", null, "Sample.cs" },
+ { "Test/resources/foo.de.resx", "RandomName", "Sample.cs" },
+
+ // W/o dependent file
+ { "foo.de.resx", null, null },
+ { "foo.de.resx", "RandomName", null },
+
+ { "Test/resources/foo.de.resx", null, null },
+ { "Test/resources/foo.de.resx", "RandomName", null }
+ };
+
+ non_resx_no_culture_files = new string [,] {
+ { "foo.txt", null, null },
+ { "foo.txt", "RandomName", null },
+
+ { "Test/resources/foo.txt", null, null },
+ { "Test/resources/foo.txt", "RandomName", null }
+ };
+
+ non_resx_with_culture_files = new string [,] {
+ { "foo.de.txt", null, null },
+ { "foo.de.txt", "RandomName", null },
+
+ { "Test/resources/foo.de.txt", null, null },
+ { "Test/resources/foo.de.txt", "RandomName", null }
+ };
+
+ }
+
+ [Test]
+ public void TestNoRootNamespaceNoCulture ()
+ {
+ CheckResourceNames (resx_no_culture_files, new string [] {
+ // w/ dependent file
+ "Mono.Tests.Sample", "Mono.Tests.Sample",
+ "Mono.Tests.Sample", "Mono.Tests.Sample",
+
+ // W/o dependent file
+ "foo", "foo" ,
+ "Test.resources.foo", "Test.resources.foo"}, null);
+ }
+
+ [Test]
+ public void TestWithRootNamespaceNoCulture ()
+ {
+ //FIXME: How does LogicalName affect things??
+ CheckResourceNames (resx_no_culture_files, new string [] {
+ // With dependent file
+ "Mono.Tests.Sample", "Mono.Tests.Sample",
+ "Mono.Tests.Sample", "Mono.Tests.Sample",
+ // W/o dependent file
+ "RN1.RN2.foo", "RN1.RN2.foo",
+ "RN1.RN2.Test.resources.foo", "RN1.RN2.Test.resources.foo"},
+ "RN1.RN2");
+ }
+
+ [Test]
+ public void TestNoRootNamespaceWithCulture ()
+ {
+ CheckResourceNames (resx_with_culture_files, new string [] {
+ // With dependent file
+ "Mono.Tests.Sample.de", "Mono.Tests.Sample.de",
+ "Mono.Tests.Sample.de", "Mono.Tests.Sample.de",
+ // W/o dependent file
+ "foo.de", "foo.de",
+ "Test.resources.foo.de", "Test.resources.foo.de" }, null);
+ }
+
+ [Test]
+ public void TestWithRootNamespaceWithCulture ()
+ {
+ CheckResourceNames (resx_with_culture_files, new string [] {
+ // With dependent file
+ "Mono.Tests.Sample.de", "Mono.Tests.Sample.de",
+ "Mono.Tests.Sample.de", "Mono.Tests.Sample.de",
+ // W/o dependent file
+ "RN1.RN2.foo.de", "RN1.RN2.foo.de",
+ "RN1.RN2.Test.resources.foo.de", "RN1.RN2.Test.resources.foo.de"},
+ "RN1.RN2");
+ }
+
+ [Test]
+ public void TestNonResxNoRootNamespaceWithCulture ()
+ {
+ CheckResourceNames (non_resx_with_culture_files, new string [] {
+ "de\\foo.txt", "de\\foo.txt",
+ "de\\Test.resources.foo.txt", "de\\Test.resources.foo.txt"}, null);
+ }
+
+ [Test]
+ public void TestNonResxWithRootNamespaceWithCulture ()
+ {
+ CheckResourceNames (non_resx_with_culture_files, new string [] {
+ "de\\RN1.RN2.foo.txt", "de\\RN1.RN2.foo.txt",
+ "de\\RN1.RN2.Test.resources.foo.txt", "de\\RN1.RN2.Test.resources.foo.txt"},
+ "RN1.RN2");
+ }
+
+ [Test]
+ public void TestNonResxNoRootNamespaceNoCulture ()
+ {
+ CheckResourceNames (non_resx_no_culture_files, new string [] {
+ "foo.txt", "foo.txt",
+ "Test.resources.foo.txt", "Test.resources.foo.txt"}, null);
+ }
+
+ [Test]
+ public void TestNonResxWithRootNamespaceNoCulture ()
+ {
+ CheckResourceNames (non_resx_no_culture_files, new string [] {
+ // With dependent file
+ "RN1.RN2.foo.txt", "RN1.RN2.foo.txt",
+ "RN1.RN2.Test.resources.foo.txt", "RN1.RN2.Test.resources.foo.txt"},
+ "RN1.RN2");
+ }
+
+ void CheckResourceNames (string [,] files, string [] names, string rootNamespace)
+ {
+ Assert.AreEqual (files.GetUpperBound (0) + 1, names.Length, "Number of files and names must match");
+ string projectText = CreateProject (files, rootNamespace);
+
+ Engine engine = new Engine (Consts.BinPath);
+ Project project = engine.CreateNewProject ();
+ Console.WriteLine (projectText);
+ project.LoadXml (projectText);
+ Assert.IsTrue (project.Build ("1"), "A1, Error building");
+
+ BuildItemGroup group = project.GetEvaluatedItemsByName ("ResourceNames");
+ Assert.AreEqual (names.Length, group.Count, "A2");
+ for (int i = 0; i <= files.GetUpperBound (0); i++) {
+ Assert.AreEqual (names [i], group [i].FinalItemSpec, "A3 #" + (i + 1));
+ if (files [i, 1] != null)
+ Assert.IsTrue (group [i].HasMetadata ("LogicalName"), "A4 #" + (i + 1));
+ if (files [i, 2] != null)
+ Assert.IsTrue (group [i].HasMetadata ("DependentUpon"), "A5 #" + (i + 1));
+ }
+ }
+
+ string CreateProject (string [,] files, string rootNamespace)
+ {
+ StringBuilder sb = new StringBuilder ();
+ sb.Append ("<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
+
+ sb.Append ("\t<ItemGroup>\n");
+ for (int i = 0; i <= files.GetUpperBound (0); i ++) {
+ sb.AppendFormat ("\t\t<ResourceFiles Include = \"{0}\">\n", files [i, 0]);
+ if (files [i, 1] != null)
+ sb.AppendFormat ("\t\t\t<LogicalName>{0}</LogicalName>\n", files [i, 1]);
+ if (files [i, 2] != null)
+ sb.AppendFormat ("\t\t\t<DependentUpon>{0}</DependentUpon>\n", files [i, 2]);
+ sb.AppendFormat ("\t\t</ResourceFiles>\n");
+ }
+ sb.Append ("\t</ItemGroup>\n");
+
+ sb.Append ("\t<Target Name=\"1\">\n");
+ sb.Append ("\t\t<CreateCSharpManifestResourceName ResourceFiles=\"@(ResourceFiles)\" ");
+ if (rootNamespace != null)
+ sb.AppendFormat (" RootNamespace = \"{0}\"", rootNamespace);
+ sb.Append (">\n \t\t\t<Output TaskParameter=\"ManifestResourceNames\" ItemName=\"ResourceNames\" />\n");
+ sb.Append ("\t\t</CreateCSharpManifestResourceName>\n\t</Target>\n");
+ sb.Append ("\t<UsingTask TaskName=\"Microsoft.Build.Tasks.CreateCSharpManifestResourceName\" " +
+ "AssemblyName=\"Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\"/>\n");
+ sb.Append ("</Project>");
+
+ return sb.ToString ();
+ }
+ }
+}