Generate proper Link metadata for resources in imported projects
authorLluis Sanchez <lluis@xamarin.com>
Thu, 15 Jan 2015 09:26:15 +0000 (10:26 +0100)
committerLluis Sanchez <lluis@xamarin.com>
Thu, 15 Jan 2015 10:07:49 +0000 (11:07 +0100)
When importing a project that contains embedded resources, the id
of those resources must be generated using the path relative
to the project file that includes them, not relative to the
main project file. To fix this problem, we now generate a
Link metadata property for those resources that has the correct
relative path, and later this is used to generate the correct
id for the resource. We also now store a new metadata value
for all items (DefiningProjectFullPath) which contains the
path to the project file that defines the element. This is
used to check if an item was defined in an imported file,
in which case the Link assignment may need to be made.

This fixes bug BXC 20966.

14 files changed:
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/BuildItem.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/DirectoryScanner.cs
mcs/class/Microsoft.Build.Engine/Microsoft.Build.BuildEngine/Project.cs
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_3_5.csproj
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-net_4_5.csproj
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks-xbuild_12.csproj
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks.dll.sources
mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssignLinkMetadata.cs [new file with mode: 0644]
mcs/tools/xbuild/data/12.0/Microsoft.Common.targets
mcs/tools/xbuild/data/12.0/Microsoft.Common.tasks
mcs/tools/xbuild/data/3.5/Microsoft.Common.targets
mcs/tools/xbuild/data/3.5/Microsoft.Common.tasks
mcs/tools/xbuild/data/4.0/Microsoft.Common.targets
mcs/tools/xbuild/data/4.0/Microsoft.Common.tasks

index 86f14a1a797af9fa3277bba8f7d5f61e0c361f30..f663120bf798730223c944db359caa7873771dc5 100644 (file)
@@ -315,7 +315,7 @@ namespace Microsoft.Build.BuildEngine {
                                        return;
                                }
                        }
-                       
+
                        DirectoryScanner directoryScanner;
                        Expression includeExpr, excludeExpr;
                        ITaskItem[] includes, excludes;
@@ -341,8 +341,10 @@ namespace Microsoft.Build.BuildEngine {
                        directoryScanner.Includes = includes;
                        directoryScanner.Excludes = excludes;
 
-                       if (project.FullFileName != String.Empty)
+                       if (project.FullFileName != String.Empty) {
+                               directoryScanner.ProjectFile = project.ThisFileFullPath;
                                directoryScanner.BaseDirectory = new DirectoryInfo (Path.GetDirectoryName (project.FullFileName));
+                       }
                        else
                                directoryScanner.BaseDirectory = new DirectoryInfo (Directory.GetCurrentDirectory ());
                        
index 4e8aa9a340c11eed300c2124174818cdadf26dcf..102c2de2a0fbb765cffe8e406a43afa5f369d63b 100644 (file)
@@ -38,6 +38,7 @@ namespace Microsoft.Build.BuildEngine {
                DirectoryInfo   baseDirectory;
                ITaskItem[]     includes, excludes;
                ITaskItem[]     matchedItems;
+               string projectFile;
 
                static bool _runningOnWindows;
                
@@ -82,8 +83,11 @@ namespace Microsoft.Build.BuildEngine {
 
                        string name = include_item.ItemSpec;
                        if (!HasWildcard (name)) {
-                               if (!excludedItems.ContainsKey (Path.GetFullPath(name)))
+                               if (!excludedItems.ContainsKey (Path.GetFullPath (name))) {
                                        includedItems.Add (include_item);
+                                       if (projectFile != null)
+                                               include_item.SetMetadata ("DefiningProjectFullPath", projectFile);
+                               }
                        } else {
                                if (name.Split (Path.DirectorySeparatorChar).Length > name.Split (Path.AltDirectorySeparatorChar).Length) {
                                        separatedPath = name.Split (new char [] {Path.DirectorySeparatorChar},
@@ -127,6 +131,8 @@ namespace Microsoft.Build.BuildEngine {
                                                                rec_dir += Path.DirectorySeparatorChar;
                                                        item.SetMetadata ("RecursiveDir", rec_dir);
                                                }
+                                               if (projectFile != null)
+                                                       item.SetMetadata ("DefiningProjectFullPath", projectFile);
                                                includedItems.Add (item);
                                        }
                                }
@@ -236,6 +242,11 @@ namespace Microsoft.Build.BuildEngine {
                        set { baseDirectory = value; }
                }
                
+               public string ProjectFile {
+                       get { return projectFile; }
+                       set { projectFile = value; }
+               }
+
                public ITaskItem[] Includes {
                        get { return includes; }
                        set { includes = value; }
index 065dc4c77bc490dc2cb67624f455d3d9b9c48196..1d16bbf5351c93dd8299174ac1fbc79c2a2a9e1a 100644 (file)
@@ -1429,6 +1429,10 @@ namespace Microsoft.Build.BuildEngine {
                        return default (T);
                }
 
+               internal string ThisFileFullPath {
+                       get { return this_file_property_stack.Peek (); }
+               }
+
                // Used for MSBuild*This* set of properties
                internal void PushThisFileProperty (string full_filename)
                {
index 7b51224340a73946ce314e7db63cd80d441983d9..563a9b7e882b110e1bd71bce3b22c0e31f769dc9 100644 (file)
     <Compile Include="Microsoft.Build.Tasks\AspNetCompiler.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssemblyResolver.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignCulture.cs" />\r
+    <Compile Include="Microsoft.Build.Tasks\AssignLinkMetadata.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignProjectConfiguration.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignTargetPath.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CallTarget.cs" />\r
index a811de97c38850fab72db3b2ac1bad0d5a68fa8a..6806165daca47710c7e802796aebffa80a196205 100644 (file)
     <Compile Include="Microsoft.Build.Tasks\AssignCulture.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignProjectConfiguration.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignTargetPath.cs" />\r
+    <Compile Include="Microsoft.Build.Tasks\AssignLinkMetadata.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CallTarget.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CodeTaskFactory.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CombinePath.cs" />\r
index 418001ee69e847aa3954916c36d6d366d93967c5..dfe90616d205d99a259c7cf69e3e7f8234f5dc77 100644 (file)
     <Compile Include="Microsoft.Build.Tasks\AssignCulture.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignProjectConfiguration.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\AssignTargetPath.cs" />\r
+    <Compile Include="Microsoft.Build.Tasks\AssignLinkMetadata.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CallTarget.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CombinePath.cs" />\r
     <Compile Include="Microsoft.Build.Tasks\CommandLineBuilderExtension.cs" />\r
index e937bd5cc73fa7635b4165aa6c2d700776290935..2261d7af65e926d8406757b5752a382fcdd75949 100644 (file)
@@ -7,6 +7,7 @@ Microsoft.Build.Tasks/AppDomainIsolatedTaskExtension.cs
 Microsoft.Build.Tasks/AspNetCompiler.cs
 Microsoft.Build.Tasks/AssemblyResolver.cs
 Microsoft.Build.Tasks/AssignCulture.cs
+Microsoft.Build.Tasks/AssignLinkMetadata.cs
 Microsoft.Build.Tasks/AssignProjectConfiguration.cs
 Microsoft.Build.Tasks/AssignTargetPath.cs
 Microsoft.Build.Tasks/CallTarget.cs
diff --git a/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssignLinkMetadata.cs b/mcs/class/Microsoft.Build.Tasks/Microsoft.Build.Tasks/AssignLinkMetadata.cs
new file mode 100644 (file)
index 0000000..b306990
--- /dev/null
@@ -0,0 +1,87 @@
+//
+// AssignLinkMetadata.cs
+//
+// Author:
+//   Lluis Sanchez (lluis@xamarin.com)
+//
+// Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using System.Collections.Generic;
+
+namespace Microsoft.Build.Tasks {
+       public class AssignLinkMetadata : TaskExtension {
+       
+               ITaskItem[]     assignedFiles;
+               ITaskItem[]     files;
+       
+               public AssignLinkMetadata ()
+               {
+               }
+               
+               public override bool Execute ()
+               {
+                       if (files == null || files.Length == 0)
+                               //nothing to do
+                               return true;
+
+                       List<ITaskItem> outFiles = new List<ITaskItem> ();
+
+                       for (int i = 0; i < files.Length; i ++) {
+                               string file = files [i].ItemSpec;
+                               string link = files [i].GetMetadata ("Link");
+                               string definingProjectPath = files [i].GetMetadata ("DefiningProjectFullPath");
+
+                               if (String.IsNullOrEmpty (link) && Path.IsPathRooted (file) && !string.IsNullOrEmpty (definingProjectPath)) {
+                                       file = Path.GetFullPath (file);
+                                       var projectDir = Path.GetFullPath (Path.GetDirectoryName (definingProjectPath));
+                                       if (projectDir.Length == 0 || projectDir [projectDir.Length - 1] != Path.DirectorySeparatorChar)
+                                               projectDir += Path.DirectorySeparatorChar;
+                                       if (file.StartsWith (projectDir)) {
+                                               // The file is in the same folder or a subfolder of the project that contains it.
+                                               // Use the relative path wrt the containing project as link.
+                                               var outFile = new TaskItem (files [i]);
+                                               outFile.SetMetadata ("Link", file.Substring (projectDir.Length));
+                                               outFiles.Add (outFile);
+                                       }
+                               }
+                       }
+
+                       assignedFiles = outFiles.ToArray ();
+
+                       return true;
+               }
+               
+               [Output]
+               public ITaskItem[] OutputItems {
+                       get { return assignedFiles; }
+               }
+               
+               public ITaskItem[] Items {
+                       get { return files; }
+                       set { files = value; }
+               }
+       }
+}
index 2642ba02696342bfcc17e4c5fe5beaa453041293..fdf43ccd488b2b7019eda123ea3625c93ae92179 100644 (file)
                        Text="OutDir property must end with a slash."/>
        </Target>
 
-       <Target Name="PrepareForBuild">
+       <PropertyGroup>
+               <PrepareForBuildDependsOn>AssignLinkMetadata</PrepareForBuildDependsOn>
+       </PropertyGroup>
+       <Target Name="PrepareForBuild" DependsOnTargets="$(PrepareForBuildDependsOn)">
                <Message Importance="High" Text="Configuration: $(Configuration) Platform: $(Platform)"/>
 
                <!-- Look for app.config, if $(AppConfig) is specified, then use that. Else look in
                />
        </Target>
 
+       <Target Name="AssignLinkMetadata">
+           <AssignLinkMetadata Items="@(EmbeddedResource)" Condition="'@(EmbeddedResource)' != '' and '%(EmbeddedResource.DefiningProjectFullPath)' != '$(MSBuildProjectFullPath)'">
+             <Output TaskParameter="OutputItems" ItemName="_EmbeddedResourceWithLinkAssigned" />
+           </AssignLinkMetadata>
+
+           <ItemGroup>
+               <EmbeddedResource Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+               <EmbeddedResource Include="@(_EmbeddedResourceWithLinkAssigned)" />
+               <_EmbeddedResourceWithLinkAssigned Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+           </ItemGroup>
+       </Target>
+
        <PropertyGroup>
                <GetFrameworkPathsDependsOn />
        </PropertyGroup>
index df28127ce429fedd920dae4e9cf0091c0280bb9b..14047e5e8584240940b7a2df446782c58a872c92 100644 (file)
@@ -1,6 +1,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
        <UsingTask TaskName="Microsoft.Build.Tasks.AL"                  AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignTargetPath"    AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+       <UsingTask TaskName="Microsoft.Build.Tasks.AssignLinkMetadata"  AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignCulture"       AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignProjectConfiguration"  AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.CallTarget"          AssemblyName="Microsoft.Build.Tasks.v12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
index 03935bdba31ec33ec419f65d0623537c3e7ae6da..5e5a5679ebaca17d4619e5b3cdd5546953be90ee 100644 (file)
                        Text="OutDir property must end with a slash."/>
        </Target>
 
-       <Target Name="PrepareForBuild">
+       <PropertyGroup>
+               <PrepareForBuildDependsOn>AssignLinkMetadata</PrepareForBuildDependsOn>
+       </PropertyGroup>
+       <Target Name="PrepareForBuild" DependsOnTargets="$(PrepareForBuildDependsOn)">
                <Message Importance="High" Text="Configuration: $(Configuration) Platform: $(Platform)"/>
 
                <!-- Look for app.config, if $(AppConfig) is specified, then use that. Else look in
                />
        </Target>
 
+       <Target Name="AssignLinkMetadata">
+           <AssignLinkMetadata Items="@(EmbeddedResource)" Condition="'@(EmbeddedResource)' != '' and '%(EmbeddedResource.DefiningProjectFullPath)' != '$(MSBuildProjectFullPath)'">
+             <Output TaskParameter="OutputItems" ItemName="_EmbeddedResourceWithLinkAssigned" />
+           </AssignLinkMetadata>
+
+           <ItemGroup>
+                       <EmbeddedResource Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+                       <EmbeddedResource Include="@(_EmbeddedResourceWithLinkAssigned)" />
+                       <_EmbeddedResourceWithLinkAssigned Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+           </ItemGroup>
+       </Target>
+
        <PropertyGroup>
                <GetFrameworkPathsDependsOn />
        </PropertyGroup>
index 9f373e5e7691de96f74161a891ec6f9b670945a3..68fcc7708fed1d172f3725ef6ea8041146aed5c3 100644 (file)
@@ -1,6 +1,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
        <UsingTask TaskName="Microsoft.Build.Tasks.AL"                  AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignTargetPath"    AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+       <UsingTask TaskName="Microsoft.Build.Tasks.AssignLinkMetadata"  AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignCulture"       AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignProjectConfiguration"  AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.CallTarget"          AssemblyName="Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
index 0017f5c3c3bbeb51a0589b52c6dd9546db3bba2f..832b54fbefc1598284a5bffcc8fee189913787da 100644 (file)
                        Text="OutDir property must end with a slash."/>
        </Target>
 
-       <Target Name="PrepareForBuild">
+       <PropertyGroup>
+               <PrepareForBuildDependsOn>AssignLinkMetadata</PrepareForBuildDependsOn>
+       </PropertyGroup>
+       <Target Name="PrepareForBuild" DependsOnTargets="$(PrepareForBuildDependsOn)">
                <Message Importance="High" Text="Configuration: $(Configuration) Platform: $(Platform)"/>
 
                <!-- Look for app.config, if $(AppConfig) is specified, then use that. Else look in
                />
        </Target>
 
+       <Target Name="AssignLinkMetadata">
+           <AssignLinkMetadata Items="@(EmbeddedResource)" Condition="'@(EmbeddedResource)' != '' and '%(EmbeddedResource.DefiningProjectFullPath)' != '$(MSBuildProjectFullPath)'">
+             <Output TaskParameter="OutputItems" ItemName="_EmbeddedResourceWithLinkAssigned" />
+           </AssignLinkMetadata>
+
+           <ItemGroup>
+                       <EmbeddedResource Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+                       <EmbeddedResource Include="@(_EmbeddedResourceWithLinkAssigned)" />
+                       <_EmbeddedResourceWithLinkAssigned Remove="@(_EmbeddedResourceWithLinkAssigned)" />
+           </ItemGroup>
+       </Target>
+
        <PropertyGroup>
                <GetFrameworkPathsDependsOn />
        </PropertyGroup>
index 9f143ef8526010abd2fa5ed70b3ccb88a63b786c..1063058785d6a70ada1fab267c698e829747addd 100644 (file)
@@ -1,6 +1,7 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
        <UsingTask TaskName="Microsoft.Build.Tasks.AL"                  AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignTargetPath"    AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+       <UsingTask TaskName="Microsoft.Build.Tasks.AssignLinkMetadata"  AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignCulture"       AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.AssignProjectConfiguration"  AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        <UsingTask TaskName="Microsoft.Build.Tasks.CallTarget"          AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />