17

I'm trying to create a post build file copy step in VS2010 which handles path macros when they have embedded spaces. I've tried surrounding the copy commands in double quotes but I get error from when copy is invoked if $(SolutionDir) contains a space. the echoed command line in the error message does not show the double quotes.

copy "$(SolutionDir)$(Configuration)\*" "$(TargetDir)"

I also tried separately \" and "" but both of these cause the 2 character escape sequence to appear in the echoed command line? How does one properly escape a double quote in a build step?

Rob
  • 4,927
  • 12
  • 49
  • 54
JonN
  • 2,498
  • 5
  • 33
  • 49
  • Sorry, but why do you want to punish yourself in this way. Move your solution to a path without spaces. – Steve Jul 20 '12 at 21:53
  • Try escaping the speechmarks by using `%22` instead of `"` – keyboardP Jul 20 '12 at 21:56
  • @Steve Mine project location doesn't have spaces but another team member unknowingly placed his workspace under "Documents/Microsoft Visual Studio\Projects". Looking for a solution to not having solution break based on its location. – JonN Jul 20 '12 at 22:08
  • Could you try with a batch file? Passing the arguments inside as %1 %2 and using quotation marks if needed inside the batch? – Steve Jul 20 '12 at 22:10
  • @keyboardP Tried %22 and still get error. Error 1 The command "copy %22C:\NGLS\Debug\*%22 %22C:\NGLS\DauServer\bin\Debug\%22" exited with code 1. – JonN Jul 20 '12 at 22:10
  • @Steve I'd have the same quoting issue passing the parameters to a batch file. The problem is I need to get VS2010 to put quotes in its command line no matter if I'm calling a batch file or a copy command. – JonN Jul 20 '12 at 22:14
  • Putting quotes around macros (as you attempted to do) should work, are you sure the failure isn't due to some other problem? For example, `"$(SolutionDir)$(Configuration)*"` should probably have a backslash between $(Configuration) and the asterisk. – Frank Boyne Jul 20 '12 at 23:12
  • In C++ projects the default value for the output Directory (aka TargetDir) is `$(SolutionDir)$(Configuration)\\` so unless your project has a different value for Output Directory your copy is going to copy a directory onto itself. Assuming of course that you're building a C++ project here. – Frank Boyne Jul 20 '12 at 23:19

7 Answers7

12

I was having trouble using double quotes with a pre-build event command in Visual Studio. I have seen the batch file solutions to this problem, but it seems a batch file would not solve all problems and is not elegant. I found the solution was to put a space before the closing double quote. The details are as follows.

The following command worked, but would not support spaces in the path:

subwcrev $(SolutionDir) $(SolutionDir)subwcrev_template.txt $(SolutionDir)version.h

I have little control over where other developers will place the solution, so I had to support spaces in the path. Trying to use quotes around paths to support spaces, I came up with the following command. It always fails.

subwcrev "$(SolutionDir)" "$(SolutionDir)subwcrev_template.txt" "$(SolutionDir)version.h"

Almost by accident, I found the solution, put a space between the last character of the path and the double quote.

subwcrev "$(SolutionDir) " "$(SolutionDir)subwcrev_template.txt " "$(SolutionDir)version.h "

This worked. I tested this in AVR Studio 6.1, which uses a Visual Studio Shell.

Seth
  • 121
  • 1
  • 4
  • Sweet, in VS2017 this suggestion worked whereas escaping with backslash or triple quotes or using XML entities failed utterly. – Søren Boisen Feb 25 '19 at 11:32
  • 2
    It's because `"$(SolutionDir)"` resolves to `"some folder\"` thus the escaping your quote; `"$(SolutionDir)\"` will work though. – Vedran Dec 02 '19 at 08:03
10

Visual Studio project files are XML files. Some special characters, such as the double quote, have to be escaped by using named entities. I think they're similar to what's used for encoding strings to html.

MSDN has a reference on How To Use Reserved XML Characters in Project Files. In your example, all you would need to do to accomplish your copy is this in the .csproj/.vbproj file:

copy "$(SolutionDir)$(Configuration)\*" "$(TargetDir)"

That will wrap both paths in double quotes. You'll get errors when referencing paths with spaces and that's why the double quotes are required.

Jeff LaFay
  • 12,882
  • 13
  • 71
  • 101
6

You need to put a double quote within two double quotes.

Example of a copy file in a post build step: copy /Y """C:\source path with spaces\somefile.txt""" """C:\destination path with spaces\"""

Rob
  • 4,927
  • 12
  • 49
  • 54
JoshL
  • 141
  • 1
  • 4
6

I couldn't get the other answers to work.

I finally just escaped the last "\":

"$(TargetDir)\"
Rick Love
  • 12,519
  • 4
  • 28
  • 27
  • 2
    I could not understand the reason of escaping the double quote but it worked. – Ali Apr 08 '15 at 03:25
  • 2
    Very rarely, we software developers have to do something just because it works. It makes you feel dirty without a logical reason to do so. But in this case, it is due to a design limitation (aka a bug). Hopefully MS will fix this in the future. – Rick Love Feb 10 '16 at 18:17
  • 3
    @Ali The \ isn't escaping the double quote but rather unescaping the prior trailing \ from $(TargetDir) so that it isn't interpreted as an escape character itself for the double quote and instead as a normal independent backslash. – Matt Arnold Oct 25 '19 at 13:55
  • Just to add to my previous comment, unfortunately using the C# verbatim syntax of `@` before the string, does not make \ a literal character in this context and instead just includes the `@` in the string! – Matt Arnold Oct 25 '19 at 14:02
5

Please, oh please don't use post build events.
Instead, use the power of MSBuild's AfterBuild target:

Right click on your project and select Edit Project File. Add an AfterBuild event:

  <Target Name="AfterBuild">
    <ItemGroup>
      <FilesToCopy Include="$(SolutionDir)$(Configuration)\*" />
    </ItemGroup>
    <Copy SourceFiles="@(FilesToCopy)"
          DestinationFolder="$(TargetDir)"
          OverwriteReadOnlyFiles="true" SkipUnchangedFiles="false" Condition="'@(FilesToCopy)' != ''" />
  </Target>

Unlike the PostBuildEvent which executes by raw cmd.exe, BeforeBuild/AfterBuild targets run by managed code, which ensures more robust execution, better maintainability and traceability.

KMoraz
  • 14,004
  • 3
  • 49
  • 82
1

It is possible to just separate the trailing \ from the " with a . like so:

"$(TargetDir)."

The . just means "the current directory" (as opposed to .. to mean "the directory one level up from the current directory"); I've never actually had a use for it until this problem hit me too!

Matt Arnold
  • 668
  • 2
  • 8
  • 21
0

I had another situation. Running a tool inside the Solution Dir with two parameters containing paths with spaces. This was my solution:

"$(SolutionDir)Tool.exe" --solution-dir "$(SolutionDir)\" --project-dir "$(ProjectDir)\"

Put double quotes on the inital command, use double quotes on all paths AND use a backslash before the last double quote of each path.

KoalaBear
  • 2,755
  • 2
  • 25
  • 29