Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a Postbuild event to a project

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.
like image 208
Alfons Avatar asked Mar 19 '23 14:03

Alfons


2 Answers

var propertyGroupElement = project.Xml.CreatePropertyGroupElement();
project.Xml.AppendChild(propertyGroupElement);
propertyGroupElement.AddProperty(PreBuildEventFixture, PreBuildEvent);
propertyGroupElement.AddProperty(PostBuildEventFixture, PostBuildEvent);
like image 183
Ilya Kozhevnikov Avatar answered Mar 24 '23 07:03

Ilya Kozhevnikov


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.

like image 35
Mihai Avatar answered Mar 24 '23 09:03

Mihai