I have a web app which I’m compiling using steal, and then I just want to copy the files from it needed for production use, but I need to preserve the directory structure. So for example, the directory looks like this after running steal’s build (which compiles js/css into the production.js/css files):
\WebApp\index.html
\WebApp\app\img\a.png
\WebApp\app\img\b.png
\WebApp\app\js\foo.js
\WebApp\app\js\bar.js
\WebApp\app\css\base.css
\WebApp\app\css\app.css
\WebApp\app\css\widget1.css
\WebApp\app\production.js
\WebApp\app\production.css
\WebApp\steal\steal.js
\WebApp\steal\README.md
\WebApp\steal\build\build.js
Out of this, I want to copy only a few specific files to the same dir structure:
\artifacts\staging\www\index.html
\artifacts\staging\www\app\img\a.png
\artifacts\staging\www\app\img\b.png
\artifacts\staging\www\app\production.js
\artifacts\staging\www\app\production.css
\artifacts\staging\www\steal\steal.js
Ideally I’d have something like this:
<PropertyGroup>
<WorkingDir>WebApp\</WorkingDir>
<OutputDir>artifacts\staging\www\</OutputDir>
</PropertyGroup>
...
<ItemGroup>
<CopyFiles Remove="@(CopyFiles)" /> <!-- clean existing items -->
<CopyFiles Condition="'$(Configuration)'=='Debug'"
Include="$(WorkingDir)\**\*.*"
Exclude="$(WorkingDir)\**\.svn\**" />
<CopyFiles Condition="'$(Configuration)'=='Release'"
Include="$(WorkingDir)\index.html;$(WorkingDir)\app\img\**\*.*;$(WorkingDir)\app\production.*;$(WorkingDir)\steal\steal.js;"
Exclude="$(WorkingDir)\**\.svn\**" />
</ItemGroup>
<Copy SourceFiles="@(CopyFiles)"
DestinationFolder="@(CopyFiles->'$(OutputDir)\%(RecursiveDir)%(Filename)%(Extension)')" />
The problem of course the directory structure isn’t preserved, and I actually just get all of the files into the $(OutputDir) with no sub-directories. %(RecursiveDir) is the expansion of ** but since I’ve explicitly specified most paths, it doesn’t actually take effect.
Now I know I can brute force this with a bunch of copy tasks and itemgroups, but that introduces its own problems, aside from being ugly. For one, it’s error-prone, since if someone wants to add an item they have to be sure to use a unique itemgroup name (this build script is big and does many other tasks), and ensure several lines are all in sync.
There must be a better way than this?
<ItemGroup>
<IndexFiles Include="$(WorkingDir)\index.html" />
<ImgFiles Include="$(WorkingDir)\app\img\**\*.*" />
<AppFiles Include="$(WorkingDir)\app\production.*" />
...
</ItemGroup>
<Copy SourceFiles="@(IndexFiles)"
DestinationFolder="@(IndexFiles->'$(OutputDir)\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(ImgFiles)"
DestinationFolder="@(ImgFiles->'$(OutputDir)\app\img\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(AppFiles)"
DestinationFolder="@(AppFiles->'$(OutputDir)\app\%(RecursiveDir)%(Filename)%(Extension)')" />
....
I had the same problem, and after some struggling I managed to do it. The key is to specify folders you want to include, after “**”.
As a result, the output directory will contain the app folder with all subdirectories of “img”, and all files named “production”.
As a note- the part with “RecursiveDir” remains unchanged.