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
copy "$(ProjectDir)app.config.$(ConfigurationName)" "$(TargetDir)\$(ProjectName).exe.config
The system cannot find the file specified.
var propertyGroupElement = project.Xml.CreatePropertyGroupElement();
project.Xml.AppendChild(propertyGroupElement);
propertyGroupElement.AddProperty(PreBuildEventFixture, PreBuildEvent);
propertyGroupElement.AddProperty(PostBuildEventFixture, PostBuildEvent);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With