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

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

  • Comments 11

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:\Windows\Microsoft.NET\Framework\v4.0.30128\Microsoft.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:\Users\danmose\Documents\Visual Studio 2010\Projects\WindowsForms Application3\WindowsFormsApplication3.csproj]
D:\Windows\Microsoft.NET\Framework\v4.0.30128\Microsoft.Common.targets(1835,9): error MSB6003: The specified task executable "ResGen.exe" could not be run. The filename or extension is too long [D:\Users\danmose\Documents\Visual Studio 2010\Projects\resgen\WindowsFormsApplication3\WindowsFormsApplication3.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 Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client”. Copy that directory.

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

Subst j: "C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client”

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.net\framework\v4.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

Leave a Comment
  • Please add 2 and 7 and type the answer here:
  • Post
  • Thanks for sharing this useful information. This is very informative and will make a lot of lives easier when they encounter this error.

    Amy Cameron

    BuildMySiteforFree.com

  • Dan,

    It looks like we are having this problem when trying to upgrade to the newly released version of VS.NET 2010 Ultimate.  We didn't try upgrading until the official release, so I'm guessing that either a case was missed in the final release, or the fixes never made it to RTM.  Changing to 4.0 fixes the problem, but we're not able to do that at the moment due to our deployment infrastructure.  I don't know that the reference path change is a viable solution, either.  I'll try the relativedir workaround and let you know how that works.

    In the meantime, are there any other workarounds for this?

  • Same as David. We have a large 16-project solution that we didn't try upgrading until the RTM. We are getting this same error.

  • I am also getting a related problem [ not resgen command line length ] running resgen.exe.   It happens on all projects in my solution, and I've submitted this as a bug in MS Connect.  Unfortunately, it's a severe show stopper for me, with the only thing I can do is to go back to VS2008.

    I can't deliver stuff in 4.0

    As others have said, any work arounds?

    My post on MS Build forum: http://social.msdn.microsoft.com/Forums/en-US/msbuild/thread/e5900710-9849-4d10-aa28-48b734d06bf2

    and on Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/552575/build-fails-when-targeted-for-3-5-but-succeeds-with-4-0

  • Is this fixed in SP1 (beta)? And if so, is the beta solid enough for production code?

    thanks - dave

  • Hi Dan,

    the workaround of creating a "BucketizeResources" subtask was suggested to us from MS support, but we had to do a small change: in our projects we have same image files with "Build action" = "Embedded Resource".

    With the workaround we received this error message:

    "The item xxx was specified more than once in the "Resources" parameter and both items had the same value xxx for the "LogicalName" metadata.  Duplicate items are not supported by the "Resources" parameter unless they have different values for the "LogicalName" metadata.

    My workaround was: also modify the task "_GenerateCompileInputs":

       <Target Name="_GenerateCompileInputs">

               ...

               <_CoreCompileResourceInputs Include="@(EmbeddedResource->'%(OutputResource)')" Condition="'%(EmbeddedResource.BucketNumber)' != '' and '%(EmbeddedResource.WithCulture)' == 'false' and '%(EmbeddedResource.Type)' == 'Resx'" />

               <_CoreCompileResourceInputs Include="@(EmbeddedResource)" Condition="'%(EmbeddedResource.BucketNumber)' != '' and '%(EmbeddedResource.WithCulture)' == 'false' and '%(EmbeddedResource.Type)' == 'Non-Resx' " />

              ...

       </Target>

    At least this worked around the error, and our app seems to work fine, too. Hope you can confirm this.

    Best regards

    Wolfgang

  • Still not fixed - July 2011 :(

  • Still not fixed - August 2011 :(

  • Do not wait a fix, they decided in another post not to fix it until a next release! why would they fix it at this release while they like to force people to buy thier new releases for a fix!

  • Is there an easy way to determine if the code added is actually being run?

    I've tried enabling with "reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\4.0" /v DebuggerEnabled /d true". It states that the operation completed successfully, but the debug option doesn't become available in the command line.

    Thanks Marc

  • Thank you very much! This blog is very useful. Best regards from Hungary!

Page 1 of 1 (11 items)