If your build fails with “MSB6002: The command-line for the “ResGen” task is too long”

Visual Studio Blog

UPDATE: This issue is fixed in .NET 4.5. As always, feedback is welcome! Please leave your comments in this blog post and report any bugs on Microsoft Connect.

 

If you have the RC build of VS2010, you are targeting the 3.5, 3.0, or 2.0 Framework, you have a VB or C# project which has a lot of references, and you also have a lot of .resx files, your build may break with an error message like this

D:WindowsMicrosoft.NETFrameworkv4.0.30128Microsoft.Common.targets(1835,9): warning MSB6002: The command-line for the “ResGen” task is too long.
Command-lines longer than 32000 characters are likely to fail. Try reducing the length of the command-line by breaking down the call to “ResGen” into multiple calls with fewer parameters per call. [D:UsersdanmoseDocumentsVisual Studio 2010ProjectsWindowsForms Application3WindowsFormsApplication3.csproj]
D:WindowsMicrosoft.NETFrameworkv4.0.30128Microsoft.Common.targets(1835,9): error MSB6003: The specified task executable “ResGen.exe” could not be run. The filename or extension is too long [D:UsersdanmoseDocumentsVisual Studio 2010ProjectsresgenWindowsFormsApplication3WindowsFormsApplication3.csproj]

This bug was reported in Beta 2, but unfortunately the fix we made in time for the RC release only fixed the most common case. The other case will be fixed for the final RTM release.

Meanwhile, here’s some workarounds to get you unblocked while you use the RC build. Essentially the problem is the combined length of the paths to all your references isn’t being included when we figure out whether to break up the inputs over more than one resgen.exe command. So what we want to do is make those paths shorter. There’s three options:

(1) Retarget the project to 4.0 until RTM

Obviously, if you’re working on a serious project, that’s likely not acceptable, but if you’re just experimenting with the RC, you might be okay with that, and it’s easy to do.  Just open up your project properties, and change the dropdown (see image)

image

(2) If that’s not possible, remove any references your project has that it doesn’t actually use in the code. That might reduce the length of the command line just enough.

(3) If your build still fails, or there’s no references you can remove, this workaround should avoid the problem:

(a) Dial up the build verbosity to Normal in Tools>Options>Project>Build. Here’s the Options dialog with the dropdown circled. (You can set it back after you’re done applying the workaround)

image

(b) Build the project with the problem and look in the build log for the resgen.exe command line. You’ll see your references’ paths getting passed to resgen.exe. Find where the .NET Framework references are coming from. It’s probably something like “C:Program FilesReference AssembliesMicrosoftFramework.NETFrameworkv4.0ProfileClient”. Copy that directory.

(c) In a console window, use subst to make an unused drive letter point there. For example.,

Subst j: “C:Program FilesReference AssembliesMicrosoftFramework.NETFrameworkv4.0ProfileClient”

image

(d) Back in VS, open up the project properties and look for Reference Paths. It’s in slightly different places for VB and C# projects:

VB

clip_image004

C#

clip_image006

Add “j:” or whatever your subst drive was to the Reference Paths. (If there’s already existing entries, put it at the front.)

(e) Your build should now succeed. If it doesn’t, that means that your remaining references are still too long. You can do the same technique, using a different drive, to point to some place where several other references are

If you have more than one project with this problem, you may be able to avoid setting the ReferencePath on all of them if they don’t already have a ReferencePath. Simply set an environment variable ReferencePath with the value “j:” and launch VS from that window. Those that do already have a ReferencePath will ignore this environment variable.

(4) If that’s still not enough, you can resort to extreme measures! This means modifying the GenerateResource target so that it doesn’t try to consume all those references at once, by modifying the condition in the CoreResGen target in Microsoft.Common.targets (find the line in bold below). This file is in your %windir%microsoft.netframeworkv4.0.30128. Be sure to back up your copy – if you make a mistake, you won’t be able to build at all.

<GenerateResource
    Sources=”@(EmbeddedResource)”
    UseSourcePath=”$(UseSourcePath)”
    References=”@(ReferencePath)”
    AdditionalInputs=”$(MSBuildAllProjects)”
    NeverLockTypeAssemblies=”$(GenerateResourceNeverLockTypeAssemblies)”
    StronglyTypedClassName=”%(EmbeddedResource.StronglyTypedClassName)”
    StronglyTypedFileName=”%(EmbeddedResource.StronglyTypedFileName)”
    StronglyTypedLanguage=”%(EmbeddedResource.StronglyTypedLanguage)”
    StronglyTypedNamespace=”%(EmbeddedResource.StronglyTypedNamespace)”
    StronglyTypedManifestPrefix=”%(EmbeddedResource.StronglyTypedManifestPrefix)”
    PublicClass=”%(EmbeddedResource.PublicClass)”
    OutputResources=”@(EmbeddedResource->’$(IntermediateOutputPath)%(ManifestResourceName).resources’)”
    Condition=”‘%(EmbeddedResource.Type)’ == ‘Resx’ and ‘%(EmbeddedResource.GenerateResource)’ != ‘false'”
    SdkToolsPath=”$(TargetFrameworkSDKToolsDirectory)”
    ExecuteAsTool=”$(ResGenExecuteAsTool)”
    EnvironmentVariables=”$(ResGenEnvironment)”
    MinimalRebuildFromTracking=”$(MinimalRebuildFromTracking)”
    TrackFileAccess=”$(TrackFileAccess)”
    TrackerLogDirectory=”$(TrackerLogDirectory)”
    ToolArchitecture=”$(ResGenToolArchitecture)”
    TrackerFrameworkPath=”$(ResGenTrackerFrameworkPath)”
    TrackerSdkPath=”$(ResGenTrackerSdkPath)”>

    <Output TaskParameter=”FilesWritten” ItemName=”FileWrites”/>
    <Output TaskParameter=”StronglyTypedFileName” ItemName=”Compile”/>

    <!– Gather Sources as an output since it will contain OutputResource metadata indicating the final output resource that it was compiled into –>
    <Output TaskParameter=”Sources” ItemName=”_Temporary” />

</GenerateResource>

One extreme would be to add and ‘%(EmbeddedResource.Identity)’ != ” to the Condition – this has the effect of causing GenerateResource to batch on every single resource individually.  However, given that you have enough resources to be hitting the 32000 character command line limit, you probably don’t want to be starting up and shutting down an individual ResGen.exe for every resource – it will work but be very slow. (That might be okay, if you’re building them once, then don’t plan to modify them again.)

A more reasonable solution, if it works with your project setup, is to add the clause and ‘%(EmbeddedResource.RelativeDir)’ == ‘%(EmbeddedResource.RelativeDir)’ instead.  RelativeDir is a built-in metadata on all items that contains the directory part of the Include, so if you have your resources divided into several folders, GenerateResource would run once against each folder, instead of against all resources at once.  This will solve the problem as long as you don’t have more than 32000 characters worth of resources in a particular folder. If not, you could move them into several folders to make this work.

UPDATE (12/20/10)

Here’s an improved workaround. It’s still ugly (or uglier) but it will do the bucketizing for you – no need to set metadata manually on your EmbeddedResource items. It’s not pretty to look at, but you don’t have to grok it all.  (For those that are interested, it’s an example of an inline task.) Here’s what to do:

(1) Open up the specific project that’s hitting this error.

(2) Paste everything below between the markers, just inside the closing </Project> tag.

(3) Build. If it works, try increasing “ResourceBatchSize” (highlighted in red below) until it doesn’t build, then back off a little. Or, if it doesn’t work, reduce “ResourceBatchSize” until it does. The idea is to get a reasonably high number so the build doesn’t launch resgen too many times, but not so high that it fails.

Please do post if this works for you, or if it doesn’t.

Dan

===============================================================

  <PropertyGroup>
    <!– Set this number to the number of items you want in a batch.
      *  Keep cranking this number down until you can build.
      *  Bigger numbers yield faster performance.
      *  If the number is too big, then command line will be too long, and the build will fail. –>
    <ResourceBatchSize>10</ResourceBatchSize>
  </PropertyGroup>


  <!– This inline task adds BucketNumber metadata in groups as specified by ResourceBatchSize –>
  <UsingTask TaskName=”BucketizeResources” TaskFactory=”CodeTaskFactory” AssemblyFile=”$(MSBuildToolsPath)Microsoft.Build.Tasks.v4.0.dll”>
    <ParameterGroup>
      <ResourceBatchSize ParameterType=”System.UInt32″ Required=”true” />
      <InputItems ParameterType=”Microsoft.Build.Framework.ITaskItem[]” Required=”true” />
      <OutputItems ParameterType=”Microsoft.Build.Framework.ITaskItem[]” Output=”true” />
    </ParameterGroup>
    <Task>
      <Using Namespace=”System” />
      <Code Type=”Fragment” Language=”cs”>
        <![CDATA[
        Log.LogMessage(string.Format(“ResourceBatchsize: {0}”, ResourceBatchSize));
        uint bucketSize = Math.Max(1, ResourceBatchSize);
        uint bucket = 0;
        uint count = 0;
        Log.LogMessage(string.Format(“Batching items into buckets of size: {0}”, bucketSize));
        foreach (ITaskItem item in InputItems)
        {
            item.SetMetadata(“BucketNumber”, bucket.ToString());
            count++;
            if (count >= bucketSize)
            {
                count = 0;
                bucket++;
            }
        }
        Log.LogMessage(string.Format(“Created {0} batches”, bucket + 1));
        OutputItems = InputItems;
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <!–     Override the entire CoreResGen target: verbatim, except that we modified the condition on GenerateResource so that it batches on the value of “ResourceBucketSize”. Note, if and when you install the next version of MSBuild, this will become out of date, so you’ll need to copy the content again from microsoft.common.targets. But I should hope the bug is fixed then, so you can delete all of this workaround! –>
  <Target
    Name=”CoreResGen”
    DependsOnTargets=”$(CoreResGenDependsOn)”>

    <ItemGroup>
      <_Temporary Remove=”@(_Temporary)” />
    </ItemGroup>

    <PropertyGroup>
      <MinimalRebuildFromTracking Condition=”‘$(MinimalRebuildFromTracking)’ == ””>true</MinimalRebuildFromTracking>
      <TrackFileAccess Condition=”‘$(TrackFileAccess)’ == ””>true</TrackFileAccess>
      <TrackerLogDirectory Condition=”‘$(TrackerLogDirectory)’ == ””>$(IntermediateOutputPath)</TrackerLogDirectory>
    </PropertyGroup>

    <!– Add bucket number appropriately –>
    <BucketizeResources ResourceBatchSize=”$(ResourceBatchSize)” InputItems=”@(EmbeddedResource)”>
      <Output TaskParameter=”OutputItems” ItemName=”_Temp” />
    </BucketizeResources>

    <ItemGroup>
      <EmbeddedResource Remove=”@(EmbeddedResource)”/>
      <EmbeddedResource Include=”@(_Temp)”/>
      <_Temp Remove=”@(_Temp)”/>
    </ItemGroup>

    <GenerateResource
        Sources=”@(EmbeddedResource)”
        UseSourcePath=”$(UseSourcePath)”
        References=”@(ReferencePath)”
        AdditionalInputs=”$(MSBuildAllProjects)”
        NeverLockTypeAssemblies=”$(GenerateResourceNeverLockTypeAssemblies)”
        StronglyTypedClassName=”%(EmbeddedResource.StronglyTypedClassName)”
        StronglyTypedFileName=”%(EmbeddedResource.StronglyTypedFileName)”
        StronglyTypedLanguage=”%(EmbeddedResource.StronglyTypedLanguage)”
        StronglyTypedNamespace=”%(EmbeddedResource.StronglyTypedNamespace)”
        StronglyTypedManifestPrefix=”%(EmbeddedResource.StronglyTypedManifestPrefix)”
        PublicClass=”%(EmbeddedResource.PublicClass)”
        OutputResources=”@(EmbeddedResource->’$(IntermediateOutputPath)%(ManifestResourceName).resources’)”
        Condition=”‘%(EmbeddedResource.Type)’ == ‘Resx’ and ‘%(EmbeddedResource.GenerateResource)’ != ‘false’ and ‘%(EmbeddedResource.BucketNumber)’ != ” ”
        SdkToolsPath=”$(TargetFrameworkSDKToolsDirectory)”
        ExecuteAsTool=”$(ResGenExecuteAsTool)”
        EnvironmentVariables=”$(ResGenEnvironment)”
        MinimalRebuildFromTracking=”$(MinimalRebuildFromTracking)”
        TrackFileAccess=”$(TrackFileAccess)”
        TrackerLogDirectory=”$(TrackerLogDirectory)”
        ToolArchitecture=”$(ResGenToolArchitecture)”
        TrackerFrameworkPath=”$(ResGenTrackerFrameworkPath)”
        TrackerSdkPath=”$(ResGenTrackerSdkPath)”>

      <Output TaskParameter=”FilesWritten” ItemName=”FileWrites”/>
      <Output TaskParameter=”StronglyTypedFileName” ItemName=”Compile”/>

      <!– Gather Sources as an output since it will contain OutputResource metadata indicating the final output resource that it was compiled into –>
      <Output TaskParameter=”Sources” ItemName=”_Temporary” />

    </GenerateResource>

    <ItemGroup>
      <EmbeddedResource Remove=”@(_Temporary)” />

      <!– Add back the Sources list (with OutputResource metadata) that we output from GenerateResource into EmbeddedResource –>
      <EmbeddedResource Include=”@(_Temporary)” />
      <_Temporary Remove=”@(_Temporary)” />

      <!– EMITTED FOR COMPATIBILITY REASONS ONLY. CONSUME EMBEDDEDRESOURCE INSTEAD –>
      <ManifestResourceWithNoCulture Include=”@(EmbeddedResource->’%(OutputResource)’)” Condition=”‘%(EmbeddedResource.WithCulture)’==’false’ and ‘%(EmbeddedResource.Type)’ == ‘Resx'”>
        <EmittedForCompatibilityOnly>true</EmittedForCompatibilityOnly>
      </ManifestResourceWithNoCulture>
      <ManifestNonResxWithNoCultureOnDisk Include=”@(EmbeddedResource)” Condition=”‘%(EmbeddedResource.WithCulture)’==’false’ and ‘%(EmbeddedResource.Type)’ == ‘Non-Resx'”>
        <EmittedForCompatibilityOnly>true</EmittedForCompatibilityOnly>
      </ManifestNonResxWithNoCultureOnDisk>

      <!– EMITTED FOR COMPATIBILITY REASONS ONLY. CONSUME EMBEDDEDRESOURCE INSTEAD –>
      <ManifestResourceWithCulture Include=”@(EmbeddedResource->’%(OutputResource)’)” Condition=”‘%(EmbeddedResource.WithCulture)’==’true’ and ‘%(EmbeddedResource.Type)’ == ‘Resx'”>
        <EmittedForCompatibilityOnly>true</EmittedForCompatibilityOnly>
      </ManifestResourceWithCulture>
      <ManifestNonResxWithCultureOnDisk Include=”@(EmbeddedResource)” Condition=”‘%(EmbeddedResource.WithCulture)’==’true’ and ‘%(EmbeddedResource.Type)’ == ‘Non-Resx'”>
        <EmittedForCompatibilityOnly>true</EmittedForCompatibilityOnly>
      </ManifestNonResxWithCultureOnDisk>

    </ItemGroup>

  </Target>

===============================================================

— Dan Moseley, Developer Lead for MSBuild

0 comments

Discussion is closed.

Feedback usabilla icon