3

I'm having a problem getting xbuild to compile a web application project. We have some resource files, which are .html files.

The one that's failing currently is 'KwasantCore\Resources\HTMLEventInvitation.html'

The resource is defined in KwasantCore.csproj as

<Content Include="Resources\HTMLEventInvitation.html" />

When building on ubuntu, the file is located here:

/home/gitlab_ci_runner/gitlab-ci-runner/tmp/builds/project-1/KwasantCore/Resources/HTMLEventInvitation.html

When running xbuild, I get this error:

/home/gitlab_ci_runner/gitlab-ci-runner/tmp/builds/project-1/Kwasant.sln (default targets) ->
(Build target) ->
/home/gitlab_ci_runner/gitlab-ci-runner/tmp/builds/project-1/KwasantCore/KwasantCore.csproj (default targets) ->
/usr/lib/mono/xbuild/12.0/bin/Microsoft.Common.targets (GenerateResources target) ->

    /usr/lib/mono/xbuild/12.0/bin/Microsoft.Common.targets: error : Tool exited with code: 1. Output: Error: Invalid ResX input.
Position: Line 123, Column 5.
Inner exception: Could not find a part of the path "/home/gitlab_ci_runner/gitlab-ci-runner/tmp/builds/project-1/KwasantCore/resources/htmleventinvitation.html".

I checked the file, and it's there - the problem is case sensitivity. The resource is correctly referenced in the .csproj, so somewhere along the line, the resource is getting lowercased from 'Resources/HTMLEventInvitation.html' to 'resources/htmleventinvitation.html'

I've taken a look at the Microsoft.Common.targets file on the ubuntu box. Line 125 is something completely unrelated (it shows me </PropertyGroup>). Looking at the GenerateResources target, it shows me this:

<Target Name = "GenerateResources">
        <GenerateResource
            Sources = "@(ResxWithNoCulture)"
            UseSourcePath = "true"
            OutputResources = "@(ManifestResourceWithNoCultureName->'$(IntermediateOutputPath)%(Identity).resources')"
            Condition = "'@(ResxWithNoCulture)' != '' ">

            <Output TaskParameter = "OutputResources" ItemName = "ManifestResourceWithNoCulture"/>
            <Output TaskParameter = "FilesWritten" ItemName = "FileWrites"/>
        </GenerateResource>

        <GenerateResource
            Sources = "@(ResxWithCulture)"
            UseSourcePath = "true"
            OutputResources = "@(ManifestResourceWithCultureName->'$(IntermediateOutputPath)%(Identity).resources')"
            Condition = "'@(ResxWithCulture)' != '' ">

            <Output TaskParameter = "OutputResources" ItemName = "ManifestResourceWithCulture"/>
            <Output TaskParameter = "FilesWritten" ItemName = "FileWrites"/>
        </GenerateResource>
    </Target>

with the referenced targets being:

<CreateItem Include="@(ResourcesWithNoCulture)" Condition="'%(Extension)' == '.resx'">
    <Output TaskParameter="Include" ItemName="ResxWithNoCulture"/>
</CreateItem>

<CreateItem Include="@(ResourcesWithNoCulture)" Condition="'%(Extension)' != '.resx'">
    <Output TaskParameter="Include" ItemName="NonResxWithNoCulture"/>
</CreateItem>

<CreateItem Include="@(ResourcesWithCulture)" Condition="'%(Extension)' == '.resx'">
    <Output TaskParameter="Include" ItemName="ResxWithCulture"/>
</CreateItem>

<CreateItem Include="@(ResourcesWithCulture)" Condition="'%(Extension)' != '.resx'">
    <Output TaskParameter="Include" ItemName="NonResxWithCulture"/>
</CreateItem>

Now, this looks suspicious to me, but I can't figure out what these Include="@(ResourcesWithNoCulture)" lines are doing - a search for them elsewhere doesn't give me any hints. The fact that it's a .html file (and not .resx), makes me suspicious of the GenerateTargets target, as it's only calling the resx versions of the targets.

I'm not an expert on .targets files - can anyone give me a hand? I've googled around, but found no help. I would assume that it would be a fairly common bug, as resources aren't extremely rare (but perhaps without .resx they are).

Edit: Having looked at it again, the error related to 'GenerateResources' doesn't exactly make sense: it should be failing at 'CopyNonResxEmbeddedResources', as the resources are not .resx. They GenerateResources target shouldn't be touching the .html files - as it's only looking at 'ResxWithNoCulture' and 'ResxWithCulture'

<Target Name = "CopyNonResxEmbeddedResources"
        Condition = "'@(NonResxWithCulture)' != '' or '@(NonResxWithNoCulture)' != '' or '@(ManifestNonResxWithCulture)' != '' or '@(ManifestNonResxWithNoCulture)' != ''">

        <MakeDir Directories="$(IntermediateOutputPath)%(ManifestNonResxWithCulture.Culture)"/>
        <Copy SourceFiles = "@(NonResxWithCulture)"
            DestinationFiles = "@(ManifestNonResxWithCulture->'$(IntermediateOutputPath)%(Identity)')"
            SkipUnchangedFiles="$(SkipCopyUnchangedFiles)">
            <Output TaskParameter = "DestinationFiles" ItemName = "ManifestNonResxWithCultureOnDisk"/>
            <Output TaskParameter = "DestinationFiles" ItemName = "FileWrites"/>
        </Copy>

        <Copy SourceFiles = "@(NonResxWithNoCulture)"
            DestinationFiles = "@(ManifestNonResxWithNoCulture->'$(IntermediateOutputPath)%(Identity)')"
            SkipUnchangedFiles="$(SkipCopyUnchangedFiles)">
            <Output TaskParameter = "DestinationFiles" ItemName = "ManifestNonResxWithNoCultureOnDisk"/>
            <Output TaskParameter = "DestinationFiles" ItemName = "FileWrites"/>
        </Copy>
    </Target>

The target 'CopyNonResxEmbeddedResources' is called directly before 'GenerateResources'

Rob
  • 26,989
  • 16
  • 82
  • 98
  • I can't answer the "why", but I can offer some workarounds. Try building with `MONO_IOMAP=case xbuild ...` to get mono to fold cases. If that fails, try using [`ciopfs`](http://www.brain-dump.org/projects/ciopfs/) to give you a filesystem that is deliberately case-insensitive. – bishop Aug 13 '14 at 14:47
  • In addition to the previous comment, you could take a look at this [article on the mono-project official site](http://www.mono-project.com/archived/porting_msbuild_projects_to_xbuild/). – TooroSan Aug 15 '14 at 07:53
  • @bishop That fixed it perfectly for me. Could you add this as an answer, so I can accept it and close the question? Cheers – Rob Oct 22 '14 at 05:58
  • Hey, excellent, glad that solved it! I've posted as an answer. – bishop Oct 22 '14 at 13:38

3 Answers3

3

I don't know why this happens (my brain just can't hold any more build systems' configuration nuances), but one of the tricks I've picked up along the way is:

MONO_IOMAP=case xbuild ...

That environment variable tells Mono to be case-insensitive when searching for files. The Mono documentation uses this for solving case sensitivity porting across Windows <-> Mac <-> Linux filesystems, but the MONO_IOMAP facility provides several other filesystem and I/O mapping operations.

In the event that doesn't work, you could try ciopfs, which is a Linux user-space case-insensitive filesystem. I've never used it, though.

bishop
  • 37,830
  • 11
  • 104
  • 139
0

I can't tell you WHY it's doing that, but my solution would be to change the name of the resource to match what it's looking for.

It DOES look like it's trying to process something as Resx...

 Tool exited with code: 1. Output: Error: Invalid ResX input.

Maybe check your settings?

Kat Cox
  • 3,469
  • 2
  • 17
  • 29
0

The compiler is trying to interpret this as a resource file rather than a resource. A resource file is a .txt or .resx file which is used to specify resources (e.g. strings, images) in a specific format, rather than a resource itself.

The GenerateResource task shouldn't be run on resources at all, because its purpose is to convert .txt or .resx files into .resource files to be embedded into an assembly.

If there are no actual resource files (.txt or .resx), then you should remove that task from the project's build altogether. Otherwise you just need to make sure that only the correct files are passed to it. I can't tell you exactly how to do this without being able to see more of your configuration, but this is a common configuration task, so you should be able to find guidance on Google.

Ben Aaronson
  • 6,955
  • 2
  • 23
  • 38