2

I have a convoluted workflow like this: A build of one of the projects in Visual Studio (2013) leads to generation of XML files. These files are used by another project, as part of the framework.

However, for the XML files to be of any use, they're to be marked as embedded resource - currently done via the VS UI/form via the file's properties. This is good for manually added files.

But how do I programmatically make an autogenerated XML file an embedded resource, so that the next project can just pick it up (in a sense) and have the files be embedded in the corresponding DLL?

My current guess is to programmatically edit the .csproj file and add lines similar to the one below:

<EmbeddedResource Include="Path\To\File\MyEmbeddedResource.meta.xml" />

Is that it?

PhD
  • 11,202
  • 14
  • 64
  • 112

1 Answers1

3

You can achieve this using a linked item and - if they are both part of the same solution - an appropriate set of build dependencies.

First, build the project so that the XML file you want to add as a resource is created.

Next, in the project you want to embed the XML into, right click on the project in the Solution Explorer, choose 'Add Existing' and browse for the XML file. Before hitting the 'Add' button in the browser, click on the drop-down button to the right of it and select 'Add As Link'. This will ensure that the file will always be the current one.

Select the added file link and change the build type to 'Embedded Resource'.

Assuming that your XML comes from a project in the same solution (ignore this bit if not), then in the solution properties under Project Dependencies you can set the destination project (where the resource will go) to depend on the source project that builds the XML file.

Or, if you prefer, you can add the link yourself:

<EmbeddedResource Include="Path\To\File\MyEmbeddedResource.meta.xml">
    <Link>MyEmbeddedResource.meta.xml</Link>
</EmbeddedResource>

EDIT: after discussions...

As mentioned in the comments below, if you want to do this by direct manipulation of a project file you will need to do the following:

  1. Load the project as an Xml document of some sort.

  2. Locate the ItemGroup node that contains the project's file list, which will generally be the only one in the project that contains at least one Compile nodes.

  3. For each file, insert a new EmbedResource node with an optional Link node. The Link node is not required but this is what you get when you manually add a linked file.

  4. Save the file.

Seems fairly simple. My Xml is a bit rusty, but this code produces the exact output I was after:

// required 'using' directives: System.IO, System.Xml.Linq

public static void EmbedResourceFiles(string projectFilename, IEnumerable<string> fileList, bool makeLinks = true)
{
    // This is the namespace used by the .csproj Xml file
    XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
    // 1: Open the document
    XDocument project = XDocument.Load(projectFilename);

    // 2: Locate target ItemGroup
    var itemGroup = project.Descendants(ns + "Compile").FirstOrDefault()?.Parent;
    if (itemGroup == null)
        throw new Exception("Failed to locate correct ItemGroup node in project file");

    // 3: Insert EmbedResource nodes
    foreach (var file in fileList)
    {
        var node = new XElement(ns + "EmbeddedResource", new XAttribute("Include", file));
        if (makeLinks)
            node.Add(new XElement(ns + "Link", Path.GetFileName(file)));
        itemGroup.Add(node);
    }

    // 4: Save it, keeping a backup just in case.
    File.Copy(projectFilename, projectFilename + ".bak", true);
    project.Save(projectFilename);
}

Gather the file list however you want and pass the list to the method above. If you want to suppress creation of <Link> nodes, give it a 'false' in the third parameter. I did a few tests and it doesn't seem to make any difference either way.

Hopefully this resolves your question.

Corey
  • 15,524
  • 2
  • 35
  • 68
  • I think you misunderstood. I meant do "all" the steps programmatically. No right clicks and adding items :) – PhD Nov 03 '15 at 02:30
  • Then use the `` node that contains `` nodes, add or update the `` node that you want. Still simpler to do by hand :) – Corey Nov 03 '15 at 02:43
  • Try doing that for 300+ files, by hand :) – PhD Nov 03 '15 at 19:17
  • If the files are in a single location (or groups in a few locations) you can select all of them at once in the `Add Existing Item` file selector. That shortens the process by the degree of togetherness of the files. Otherwise... I like regular expression find/replace in my fav text editor when I have to build big lists like this. – Corey Nov 04 '15 at 00:20
  • you ought to understand - these files will NEVER be in one location if they're programmatically generated. I want them to be added "automatically" and not have ANY manual intervention. You can't assume things like "if files in same location" and other things that reduces the problem to a manual solution. Doesn't work. – PhD Nov 04 '15 at 20:16
  • OK, so you don't want to do it by hand. The `` node I posted is still the information you need. Give me a few minutes to test some code and I'll give you a more useful answer. – Corey Nov 05 '15 at 00:51