I am trying to execute a task which changes the locale/culture used at compile time for a XNA content pipeline project, and restores the original after the compilation ended. The intention is to allow proper parsing of floats in non-English machines.
So far I am using BeforeBuild and AfterBuild like this:
<UsingTask TaskName="PressPlay.FFWD.BuildTasks.SetLocale" AssemblyFile="PressPlay.FFWD.BuildTasks.dll" />
<Target Name="BeforeBuild">
<SetLocale> <!-- By default, set to 'en-US' -->
<Output TaskParameter="PrevLocale" ItemName="OrigLocale" />
</SetLocale>
</Target>
<Target Name="AfterBuild">
<SetLocale Locale="@(OrigLocale)" />
</Target>
It works properly, except when an error occurs during compilation (an invalid XML or ContentSerializer error), after which the locale will not be reset. Answers in SO are contradictory, since some say AfterBuild always executes (not in my case) and others say there's no way to ensure a target is always ran after build. I haven't found precise info regarding this around google.
I know there is the option of using PostBuildEvent
and setting it to always run, but it'd use Exec
to run the command and I suspect it would run in a separate thread, defeating its purpose (I set CurrentThread.CultureInfo
to change the locale).
So, is there a way to ensure the target is always ran? Alternatively, is there any other way to tell VS2010 to run a compilation with a specific culture?
Links to documentation explicitly clarifying the issue would be very appreciated.
-- Final solution, following Seva's answer --
XNA's content pipeline does not declare PreBuildEvent
nor PostBuildEvent
. Other required properties (RunPostBuildEvent
, PreBuildEventDependsOn
and PostBuildEventDependsOn
) aren't defined, either. However, if you define them, the content pipeline will make good use of them as in any other project.
So, the changes I had to make to the contentcsproj file were:
<!-- Added to ensure the locale is always restored -->
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<!-- Reference includes, project references and other stuff -->
<!-- ... -->
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
<!-- Customizations to change locale before compilation and restore it after -->
<!-- Needed to properly treat dots in the XMLs as decimal separators -->
<UsingTask TaskName="PressPlay.FFWD.BuildTasks.SetLocale" AssemblyFile="PressPlay.FFWD.BuildTasks.dll" />
<!-- Apparently ContentPipeline.targets does not define PreBuildEvent and PostBuildEvent -->
<!-- However, they are still used if defined -->
<Target Name="PreBuildEvent" DependsOnTargets="$(PreBuildEventDependsOn)"/>
<Target Name="PostBuildEvent" DependsOnTargets="$(PostBuildEventDependsOn)"/>
<PropertyGroup>
<PreBuildEventDependsOn>
$(PreBuildEventDependsOn);
EstablishUSLocale
</PreBuildEventDependsOn>
</PropertyGroup>
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
RestoreOriginalLocale
</PostBuildEventDependsOn>
</PropertyGroup>
<Target Name="EstablishUSLocale">
<SetLocale Locale="en-US">
<Output TaskParameter="PrevLocale" ItemName="OrigLocale" />
</SetLocale>
</Target>
<Target Name="RestoreOriginalLocale">
<SetLocale Locale="@(OrigLocale)" />
</Target>
With this solution another problem is indirectly taken care of, which is the potential issues that could arise if another project redefined BeforeBuild or AfterBuild, resulting in one of the definitions overriding the other.
You can use PostBuildEvent, because you can configure to execute it always after the build. However as you correctly noticed, using Exec task will not work here. However PostBuildEvent is actually extendable through a property called $(PostBuildEventDependsOn). You will need to define this property:
<PropertyGroup>
<PostBuildEventDependsOn>RestoreOriginalLocale</PostBuildEventDependsOn>
</PropertyGroup>
The target RestoreOriginalLocale is what you had in your AfterBuild target:
<Target Name="RestoreOriginalLocale">
<SetLocale Locale="@(OrigLocale)" />
</Target>
Your BeforeBuild target is still needed, it remains as what you wrote in your question.
To ensure PostBuildEvent is executed on failure (and thus require RestoreOriginalLocale to be executed), you need to set property RunPostBuildEvent to Always. You can do it through IDE, or by manually editing your .csproj file.