Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying build output files, preserving directory structure, gives `Illegal characters in path`

Tags:

msbuild

I am trying to get a MSBuild script to work for building and extracting for deployment a fairly large web application. (We are talking a few thousand files in several dozen directories, here. It's pretty much an inherited, legacy codebase, so not much I can do about it.) The build itself runs fine, but I can't seem to get copying the files to a drop location working properly.

Here is a snippet showing what I have in the build script:

<PropertyGroup Condition=" '$(UseBuildNumber)' == 'true' ">
  <ReleaseDirectory>$(ReleaseBaseDirectory)$(ReleaseName)\$(BuildNumber)</ReleaseDirectory>
</PropertyGroup>

<ItemGroup>
  <OutputFiles Include="
               $(SourceRoot)**\*.aspx;
               $(SourceRoot)**\*.dll;
               $(SourceRoot)**\*.gif;
               $(SourceRoot)**\*.ascx;
               " />
</ItemGroup>

<Message Text="Output files ==> @(OutputFiles)" />

<Copy
    SourceFiles="@(OutputFiles)"
    DestinationFolder="$(ReleaseDirectory)"
    SkipUnchangedFiles="false"
    />

I have put a few extra <Message/> in there to verify that the individual paths expand correctly, and both $(SourceRoot) and $(ReleaseDirectory) do indeed have the correct paths. However, in the output, I get (full path elided for brevity):

Task "Message"
  Output files ==> ...\Requirement1866**\*.aspx;...\Requirement1866**\*.dll;...\Requirement1866**\*.gif;...\Requirement1866**\*.ascx
Done executing task "Message".

Obviously, wildcard expansion has not been performed, and the following Copy then (predictably enough) fails. I'm including only one; in reality this is repeated for each of the file name wildcard patterns (which in turn are many more than I included in the build script snippet above).

Using "Copy" task from assembly "Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "Copy"
  Copying file from "*basedir*\Requirement1866**\*.aspx" to "*targetdir*\Requirement1866\*buildnumber*\*.aspx".
  Command:
  copy /y "*basedir*\Requirement1866**\*.aspx" "*targetdir*\Requirement1866\*buildnumber*\*.aspx"
d:\Builds\215\BuildType\TFSBuild.proj(120,5): error MSB3021: Unable to copy file "*basedir*\Requirement1866**\*.aspx" to "*targetdir*\Requirement1866\*buildnumber*\*.aspx". Illegal characters in path.

followed by:

  d:\Builds\215\BuildType\TFSBuild.proj(120,5): error MSB3021: Unable to copy file "*basedir*\Requirement1866**\*.aspx" to "*targetdir*\Requirement1866\*buildnumber*\*.aspx". Illegal characters in path.

The only reasonably relevant hit I have come across in my efforts at Googling this was Copy Task - Illegal characters in path, but I'm already using an <ItemGroup>, and the <CreateItem> example (adapted) didn't work at all, aborting early with an error saying that TaskParameter was unknown (sorry, I don't have the exact error message in front of me).

As a stopgap measure, I could go in and manually copy the relevant files from the build output directory to where I want them, but I want that process to be automated.

How do I, then, using MSBuild, copy files which are created during the build process into an output directory, while preserving the relative directory structure, without listing them one by one?

like image 616
user Avatar asked Jul 11 '12 14:07

user


2 Answers

Basically, ** is a single path-element and thus must be correspondingly backslash-delimited in the Include directive:

<ItemGroup>
  <OutputFiles Include="
               $(SourceRoot)\**\*.aspx;
               $(SourceRoot)\**\*.dll;
               $(SourceRoot)\**\*.gif;
               $(SourceRoot)\**\*.ascx;
               " />
</ItemGroup>

Note the difference between specifying e.g. $(SourceRoot)\**\*.aspx as opposed to the broken $(SourceRoot)**\*.aspx. If $(SourceRoot) had ended with a \, wildcard expansion probably would have worked at first too.

Second, in <Copy/>, use %(RecursiveDir):

<Copy
    SourceFiles="@(OutputFiles)"
    DestinationFolder="$(ReleaseDirectory)\%(RecursiveDir)"
    SkipUnchangedFiles="false"
    />

With these changes, it appears to work just like I want it to: files are copied to the intended target directory, and the directory structure at the source is preserved in the target location.

like image 184
user Avatar answered Nov 17 '22 03:11

user


Another Suggestion, instead of adding Includes for every file type, it is easy to add Excludes.

<ItemGroup>
  <OutputFiles Include="$(SourceRoot)**\*.*" Exclude="$(SourceRoot)**\*.cs;$(SourceRoot)**\*.resx" " />
</ItemGroup>

This way the output will include any JS, HTML, DHTML and other file types that are required.

like image 2
Dumbri Avatar answered Nov 17 '22 01:11

Dumbri