How to ignore some content files for debug builds

In our game we are using LocalizedFontProcessor to extract the needed characters from our translation to simplified chinese. This takes quite some time (5-10 minutes - 10000+ words * multiple fonts) and thus is quite annoying when we are actively working on the project resource files.

I’m now looking for a way to deactivate this during debug (or with a custom complile time constant).

One thing that comes to mind is to override LocalizedFontProcessor to make this work.

My questions now are:

  1. Is there a way to pass a compile time constant (like DEBUG) into the content build process, so I can evaluate it from my custom LocalizedFontProcessor?
  2. Is there a general way to ignore certain files / use different files in different build scenarios? (without having two mgcb files)

I want to refer to the last paragraphs in the Documentation of the MGCB (MonoGame Content Builder) which says:

To_1:

Customizing your Build Process:

When building content from your project via msbuild there are a few ways to can hook into the build process. The MonoGame.Content.Builder.targets runs a target called
BuildContent just before your project builds.

If you want to do any processing before or after this process you can use the BeforeTargets and AfterTargets mechanism provided by msbuild to run your own targest.

If you want to customise the arguements sent to the MGCB.exe as part of the build process you can use the <MonoGameMGCBAdditionalArguments> property to define those.

For example if you wanted to pass in the current project configuration you could define:

<MonoGameMGCBAdditionalArguments>-config:$(Configuration)</MonoGameMGCBAdditionalArguments>


To_2:

Preprocessor Macros:

Preprocessor macros are intended to allow conditionals within a response file.

e.g:

$if <name>=<value>
$endif

sample:

<example command line>
MGCB.exe /define:BuildEffects=No /@:example.mgcb

<example.mgcb file>
$if BuildEffects=Yes
/importer:EffectImporter
/processor:EffectProcessor
/build:Effects\custom.fx
# all other effects here....
$endif
1 Like

That is more capability than I ever expected MGCB to have. That’s pretty cool.

1 Like

Thanks, that is very helpful!

The preprocessor macros are working like a charm :slight_smile:

I’m strungling a bit with the build targets and ain’t sure whether it’s a problem of not understanding the concept.

In my .csproj file I have added the following:

      <Target Name="ContentBeforeTarget" BeforeTargets="BuildContent">
        <PropertyGroup>
          <MonoGameMGCBAdditionalArguments>-define:IncludeResxFonts=True</MonoGameMGCBAdditionalArguments>
        </PropertyGroup>
        <Message Text="ContentBeforeTarget Ran"/>
      </Target>

I had to add the property group - otherwise I would get an error with someting like “The <#text> element below the MonoGameMGCBAdditionalArguments element is unknown.”

The define does not make it’s way into MGCB. I have checked this using Process Monitor, the command line ony includes the file name, platform, quiet, outputDir and intermediateDir.
I have also tried the “-config:…” from the example - same result.

In addition I realized that the message is output to the console after all output from the actual MGCB build process.

Just place it here and it should work:

If this still doesn’t work for you, then you are using an older version of MonoGame.Content.Builder.targets (compare: old vs. new).

Additionally I created a small test project by myself to test this feature and found something interesting:

I was able to add additional command line parameter using the above mentioned technique, but it was not possible for me to successfully set the “/define” parameter from outside of the content respond file (.mgcb).

Setting the /define command only worked inside the content respond file for me (including preprocessor macros).

Tested with develop build from july 2018.

I’m not sure what’s causing this, but I would greatly like to hear / find a solution.

Thanks - one step closer now.

I have an idea why this is not working. The command line that is called by visual studio looks like this in my case:

"C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools\MGCB.exe" /@:"C:\Imagine Earth\ImagineEarth\ImagineEarth\Content\ImagineEarthContent.mgcb" /platform:Windows /define:IncludeResxFonts=True /outputDir:"bin\Windows\ImagineEarthContent" /intermediateDir:"obj\Windows\ImagineEarthContent"

The CommandLineParser class of MGCB parses the mgcb first and puts it at the top of the lines list and thus parses the whole content file before the external /define.

From my understanding this is a bug in the MonoGame Visual Studio plugin, right?

Nice catch!

You are totally right.

I tested this manually with windows cmd and it worked when setting the define tag right after MGCB.exe like this:

MGCB.exe /define:Test=No /@:ContentReference.txt

To fix this issue you need to edit the MonoGame.Content.Builder.targets file like this:

  1. Delete $(MonoGameMGCBAdditionalArguments) from this line.
  2. Place it right after $(MonoGameContentBuilderCmd) (MGCB.exe) in this line.
  3. Place the $(Header) right after the $(MonoGameMGCBAdditionalArguments) in the same line (to make the console output better readable and placing all command line parameter right behind the custom definitions [tested and works]).

I think we should raise an issue on the MonoGame repo so this becomes fixed in the 3.7 release (because it’s an easy to do fix).

Thanks for catching this @SeriousMartin :slight_smile:

2 Likes

Yes, that works :slight_smile:

Unfortunately the MonoGame Pipline Tool discards all definitions. This makes it impossible for me to use this solution.

Instead I’m using a config param now and have created my own LocalizedFontProcessor.

There is another problem with this though: Passing a config into MGCB (like in your example above) does not work when passing a mgcb file as well.
When I pass it before the file, it gets overriden by the config param in the file (the MonoGame Pipeline Tool puts a an empty “/config:” line into the file).
When it is passed after the file, it doesn’t seem to be used.

My way out of this is to call Environment.GetCommandLineArgs() and extract the relevant part in my processor manually.

I can’t confirm this:

As you can see i’m using this technique to successfully pass in the definitions parameter to a .mgcb file.
When I switch the definition from “Yes” to “No”, then the test.png image gets successfully built (as expected).

Also opening / saving / building from the pipeline tool didn’t change anything.

Maybe you declared the definitions also in the mgcb file? You don’t need to do this. Just let them come from the MonoGameMGCBAdditionalArguments tag of you project.

This also worked for me. But you are right; as soon as you are opening and saving your mgcb file through the Pipeline Tool, then also an empty “/config:” tag gets placed inside the file, which discards every other /config: command line parameter incoming.

So indeed it is not a pratical solution to always erase the /config line from the mgcb file to make that work.

But the solution to this is indeed more obvious than we are all thinking at the moment:

Just define the /config parameter directly in the pipeline tool:

I think this shouldn’t be to much work :wink:

You can then catch this in your custom processors like this:

public override string Process(string input, ContentProcessorContext context)
{
    string config = "";
    config = context.BuildConfiguration;    // Get config value from the mgcb file.
    context.Logger.LogMessage(config);      // Log it for debugging.

    // Use the config state.
    if (config == "Debug")
    {
        // Debug build.
    }
    else if (config == "Release")
    {
        // Release build.
    }
    return input;
}

Building works for me as well. The problem comes when you save the file with the Pipeline Tool.
Steps to reproduce this:

  1. Add an $if and $endif tag to the mgcb file (just like in your example).
  2. Open it with the MonoGame Pipleline Tool
  3. The entry (in your case Stone_1_Anim) will not appear in the Tree
  4. Just hit save -> The entry is removed from the .mgcb file

As far as I can see the PiplineProjectParser.SaveProject method of the Pipeline tool doesn’t write any $if or /define into the file.

That would mean that I always have to manually edit the file before doing a release build. I’m doing this far too often to not automate this. A solution could be to let some pre-build process override the config param (and maybe also platform) so I don’t have to care about this.

To sum it up: For my actual problem I found a proper workaround. Still, I’m not sure what to do about the problems we stumbled upon.
In a perfect world the Pipeline tool would read and write (and maybe even display) the /define and $if statements.
About the MGCB params I’m not sure. Should an additional /config param passed to the file always override any /config params within the mgcb-file? Probably yes.

Yes, it seems that the Pipeline Tool always generates a new mgcb file based on the properties you have set in the tool.

In the moment it seems that our approaches are only working satisfactory with dynamically created content respond files (w/o the Pipeline Tool).

I would love to help reaching this perfect world :wink:

I will take a look at the ContentItem and PipelineProjectParser classes because it not seems to be difficult to add our desired functionallity.

I will report back.

I’m not sure about that. You can use if statements arround any part of the content file. This can be a whole block of assets or just a single property of a single assert (e.g. to define different levels of sound quality).

You would probably have to remember which lines are within each if block and then sync this with the content of the new file.

Right now I’m thinking that it may make more sense to keep the if blocks for manual mgcb files only. What would be good though is to add a comment to the documentation (the one you cited).

In addition we could try to find a better solution for how MGCB deals with command line params. Thinking about this again, I’m pretty sure that command line params should be more important than settings done within the file and override those.

What do you think?

Two other issues I stumbled upon:

  1. Drag & drop doesn’t seem to be working anymore in the Pipeline Tool. Can you confirm this? If so I will have a look into the code and at least create an issue.
  2. My custom processors aren’t displayed in the Pipeline Tool. Instead it displays “Invlaid / Missing Processor” in the properties. Building is working though (with MGCB from Visual Studio and also with the Pipeline Tool). Can you reproduce this?

I hacked together a small sample yesterday and I got it working to create definitions from within the Pipeline Tool and saving and opening the mgcb file (all assets including the custom definitions were correctly loaded back to the tool and displaying the custom definitions for each file).

I would need time to create a clean version of this and fix some issues with the MGCB.exe (regarding the new implementation).

But in the moment I don’t know if this could lead to other problems, so more testing is needed.

I like your idea of giving command line parameter a higher priority than what is written in a mgcb file, but I can’t imagine how this would work for custom definitions and preprocessor macros, because they are typically defined per asset and not globally like the /config parameter.

But indeed this is something I would support for global parameter.

I will check that soon and report back.

Sorry for the late reply, I was pretty busy.

1: Pipeline Drag & Drop

I tested this with MonoGame 3.6.0.1625 and the current development branch and I can confirm that it’s not working.

But I have never used drag and drop functionallity in the Pipeline Tool before and so I can’t even say if this was a implemented feature at some point, haha. You said that it’s not working for you “anymore”, so then this is definitely a bug.

2: Invalid / Missing Processor

I’m only encountering this, when I messed the naming of my custom importers and processors (when for example the ContentProcessorAttribute and the corresponding class name differs). But then building the solution results in erros for me.

When building succeeds for you, do you get a full working and updated XNB file in the ouput directory? Or is it maybe an old fragment from previous successful builds? I recommend doing a fresh rebuild of your solution and maybe cleaning old XNB files manually to be sure.

If you can clearly say, that you are getting a fresh XNB file, despite the “Invalid / Missing Processor” information, then this is at least a bug in the display / property window of the Pipeline Tool.

Yes, I can definitely confirm that it is not working anymore (was working before).

I can also confirm this. Even with a clean solution (no output files at all) it manages to build everything successfully. It’s likely a bug in the pipeline tool and not in MGCB.

It seems that the following PR fixes the issue with invalid / missing processor:

TeamCity link:
http://teamcity.monogame.net/viewType.html?buildTypeId=MonoGame_PackagingWindows&branch_MonoGame=6450&tab=buildTypeStatusDiv

You can also try the latest dev build.

Ah, nice - I will give it a try with the 3.7.1 release.