4

When I generate a C# project (csproj file) and then compile it, msbuild somehow doesn’t recognize the variables $(ConfigurationName) and $(ProjectDir) (and others) in the pre- and postbuild event.

When I Manually move the pre- and postbuild event configuration in the generated .csproj file further downwards, then msbuild recognizes these variables correctly.

Adding the buildevent to the project is the last thing I do before saving the project.

This is how I add it:

using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;

private const string PreBuildEventFixture = "PreBuildEvent";
private const string PostBuildEventFixture = "PostBuildEvent";
private const string PreBuildEvent = "attrib -R \"$(ProjectDir)app.config\"";
private const string PostBuildEvent = "copy \"$(ProjectDir)app.config.$(ConfigurationName)\" \"$(TargetDir)\\$(ProjectName).dll.config\" \r\n attrib -R \"$(ProjectPath)\"";

public void AddBuildEvents(Project project)
{
    ProjectPropertyGroupElement propertyGroupElement = project.Xml.AddPropertyGroup();
    propertyGroupElement.AddProperty(PreBuildEventFixture, PreBuildEvent);
    propertyGroupElement.AddProperty(PostBuildEventFixture, PostBuildEvent);
}

The error I get when running the generated project through msbuild is this:

The command "copy "app.config." "\.dll.config"" exited with code 1

When I then manually edit the .csproj file (with notepad or another text editor), cut the pre-and postbuild event, and paste it below the <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> element, then msbuild builds the generated .csproj file fine.

What is the best way to add the build events to the .csproj file so it ends up after the Import element in the resulting XML?

Apparently, my current way of using [ProjectPropertyGroupElement][1] by requesting it from AddPropertyGroup of the the Xml property of the Microsoft.Build.Evaluation.Project is not.

Example Project:

using System.IO;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;

class Program
{
    private const string PreBuildEventFixture = "PreBuildEvent";
    private const string PostBuildEventFixture = "PostBuildEvent";
    private const string PreBuildEvent = "attrib -R \"$(ProjectDir)app.config\"";
    private const string PostBuildEvent = "copy \"$(ProjectDir)app.config.$(ConfigurationName)\" \"$(TargetDir)\\$(ProjectName).exe.config\" \r\n attrib -R \"$(ProjectPath)\"";

    private const string ProjectFile = @"C:\test\TestProject\TestProject.csproj";

    static void Main(string[] args)
    {
        if (!File.Exists(ProjectFile))
            throw new FileNotFoundException("ProjectFile not found");

        ProjectCollection collection = new ProjectCollection();
        Project project = collection.LoadProject(ProjectFile);

        ProjectPropertyGroupElement propertyGroupElement = project.Xml.AddPropertyGroup();
        propertyGroupElement.AddProperty(PreBuildEventFixture, PreBuildEvent);
        propertyGroupElement.AddProperty(PostBuildEventFixture, PostBuildEvent);
        project.Save();
        collection.UnloadAllProjects();
    }
}

Steps to reproduce

  • Create a new project
  • Manually add app.config.debug file which should be different to the app.debug file
  • Add the postbuildevent: copy "$(ProjectDir)app.config.$(ConfigurationName)" "$(TargetDir)\$(ProjectName).exe.config
  • See that the project build and the correct config file is applied
  • Remove the pre- and postbuild events using notepad (so not to leave any traces)
  • Run the example project
  • Reload and build the project you created.
  • Output window will now say The system cannot find the file specified.
Alfons
  • 511
  • 4
  • 17
  • It seems that the file `app.config.` doesn't exist (it is missing the `ConfigurationName`). What is your manual build event? – Patrick Hofman Aug 29 '14 at 12:09
  • no $(ConfigurationName) isn't parsed. If i move the buildevents down in the projectfile they are. – Alfons Aug 29 '14 at 12:12
  • Guessing that one of the Imports is responsible for setting up the variables, I messed with one of my project files. I had to have the post build events after this: or the variables were empty. Can you add the build events in a way to get them at the bottom? – Adam47 Aug 29 '14 at 12:46
  • Maybe add them first? – Adam47 Aug 29 '14 at 12:52
  • Adding the buildevents is te last thing i do before i call Project.Save(). The The ProjectPropertyGroupElement has a property Location, but its readonly. Im thinking that adding the buildevents using a ProjectPropertyGroupElement isnt the correct way. – Alfons Aug 29 '14 at 12:55
  • Adding them first doesn't matter, it will always add them above the of the references and the includes, while it should be added after. – Alfons Aug 29 '14 at 12:58
  • Added example project – Alfons Sep 01 '14 at 08:55
  • 2
    Have you tried using $(Configuration) instead of $(ConfigurationName)? I'm pretty sure, that MSBuild uses Configuration, not ConfigurationName. – Novakov Sep 01 '14 at 08:59
  • Hadn't tried that because it did work when moving the pre-and postbuild event down and `$(projectdir)` didnt get resolved aswell. Tried it: `echo $(ProjectDir) $(Configuration) $(TargetDir)`, and it helps some, `$(Configuration)` gets resolved. The other two where not – Alfons Sep 01 '14 at 11:08

2 Answers2

5
var propertyGroupElement = project.Xml.CreatePropertyGroupElement();
project.Xml.AppendChild(propertyGroupElement);
propertyGroupElement.AddProperty(PreBuildEventFixture, PreBuildEvent);
propertyGroupElement.AddProperty(PostBuildEventFixture, PostBuildEvent);
Ilya Kozhevnikov
  • 10,242
  • 4
  • 40
  • 70
2

Project related macros are not parsed if they are added before the project is actually constructed (constructing a project includes adding references). Instead of using $(ProjectName), the path can be constructed using solution variables (that already exist) like this :

copy "$(SolutionDir)ProjectName\app.config.$(Configuration)" "$(SolutionDir)ProjectName\bin\$(Configuration)\ProjectName.dll.config"

Note that ProjectName is the actual name of the project hardcoded, but since you are generating a project this should be easy to add.

Mihai
  • 371
  • 3
  • 14