Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

csproj: Target/Exec regression in VS 2019 #4210

Closed
bradking opened this issue Mar 5, 2019 · 13 comments
Closed

csproj: Target/Exec regression in VS 2019 #4210

bradking opened this issue Mar 5, 2019 · 13 comments
Assignees
Labels
Milestone

Comments

@bradking
Copy link

bradking commented Mar 5, 2019

Steps to reproduce

Extract CSharpExec.zip.

At a VS 2019 RC x64 command prompt with the working directory set to the extracted folder:

>msbuild CSharpExec.csproj -p:Configuration=Debug -p:VisualStudioVersion=16.0

Observe the build failure.

Expected behavior

It is expected to build successfully, as it does at a VS 2017 x64 command prompt:

>msbuild -version
...
15.9.21.664

>msbuild CSharpExec.csproj -p:Configuration=Debug -p:VisualStudioVersion=15.0
Microsoft (R) Build Engine version 15.9.21+g9802d43bc3 for .NET Framework
...(no errors)...

Actual behavior

The build fails at a VS 2019 RC x64 command prompt:

>msbuild -version
...
16.0.443.30039

>msbuild CSharpExec.csproj -p:Configuration=Debug -p:VisualStudioVersion=16.0
Microsoft (R) Build Engine version 16.0.443+g5775d0d6bb for .NET Framework
...
  echo Generating test.cs
  Generating test.cs
  setlocal copy test.cs.in test.cs ...
  Invalid parameter to SETLOCAL command
  ...
C:\...\CSharpExec.csproj(44,5): error MSB3073: The command "setlocal copy test.cs.in test.cs ..." exited with code 1.
@bradking
Copy link
Author

bradking commented Mar 5, 2019

Cc: @jgoshi

@jgoshi
Copy link

jgoshi commented Mar 5, 2019

Thank you Brad. Adding @rainersigwald @livarcocc

@rainersigwald
Copy link
Member

This appears to have been caused by #3584, because of an underlying difference in how XmlReader and XmlTextReader treat whitespace in attributes. The straightforward fix would be to back out that change, at the cost of some GC-pressure problems (that existed in v15). I can’t at the moment see a fix that preserves the current memory behavior but has better whitespace behavior.

@rainersigwald rainersigwald added this to the MSBuild 16.0 milestone Mar 5, 2019
@rainersigwald
Copy link
Member

https://github.com/Microsoft/msbuild/blob/404bb795a7a25b4465b0d48d4357c40ac4b3cf61/src/Build/Xml/XmlReaderExtension.cs#L83-L89

Setting IgnoreWhitespace = false here is insufficent; by the time the engine is thinking about creating the task, it's already gotten a whitespace-normalized (newlines mapped to single spaces) copy of the attribute. Only switching back to the XmlTextReader implementation restored the expected multiline behavior.

@rainersigwald rainersigwald self-assigned this Mar 5, 2019
@lukka
Copy link

lukka commented Mar 5, 2019

@rainersigwald is a possible fix to use explicit escaped carriage returns in the xml project files generated by CMake, i.e. 
 ? I have created this PR on CMake here.

@rainersigwald
Copy link
Member

@lukka That's a great workaround that appears to work fine in my simplified repro:

<Project>
  <Target
    Name="test_cs_debug">
    <!-- <Exec Command="echo Generating test.cs" /> -->
    <Exec Command="setlocal&#10;
copy test.cs.in test.cs&#10;
if %errorlevel% neq 0 goto :cmEnd&#10;
:cmEnd&#10;
endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone&#10;
:cmErrorLevel&#10;
exit /b %1&#10;
:cmDone&#10;
if %errorlevel% neq 0 goto :VCEnd" />
  </Target>
</Project>

But ideally we'd fix the MSBuild behavior change; it wasn't intentional.

rainersigwald added a commit to rainersigwald/msbuild that referenced this issue Mar 5, 2019
Introduce an end-to-end test using `loadProjectReadOnly` and a multiline
`Exec` task.
rainersigwald added a commit to rainersigwald/msbuild that referenced this issue Mar 5, 2019
rainersigwald added a commit to rainersigwald/msbuild that referenced this issue Mar 5, 2019
Introduce an end-to-end test using `loadProjectReadOnly` and a multiline
`Exec` task.
@rainersigwald
Copy link
Member

Another possible workaround here is to use a property instead of defining the script directly in the Command attribute:

<Project>
  <PropertyGroup>
    <CommandToCreateFile>setlocal
copy test.cs.in test.cs
if %errorlevel% neq 0 goto :cmEnd
:cmEnd
endlocal &amp; call :cmErrorLevel %errorlevel% &amp; goto :cmDone
:cmErrorLevel
exit /b %1
:cmDone
if %errorlevel% neq 0 goto :VCEnd</CommandToCreateFile>
  </PropertyGroup>
  <Target
    Name="test_cs_debug">
    <!-- <Exec Command="echo Generating test.cs" /> -->
    <Exec Command="$(CommandToCreateFile)" />
  </Target>
</Project>

@nguerrera
Copy link
Contributor

https://github.com/Microsoft/msbuild/blob/404bb795a7a25b4465b0d48d4357c40ac4b3cf61/src/Build/Xml/XmlReaderExtension.cs#L83-L89

Setting IgnoreWhitespace = false here is insufficent; by the time the engine is thinking about creating the task, it's already gotten a whitespace-normalized (newlines mapped to single spaces) copy of the attribute. Only switching back to the XmlTextReader implementation restored the expected multiline behavior.

@krwq Is there anything else we can do than use XmlTextReader and lose the perf benefit of ignoring comments?

@danmoseley
Copy link
Member

danmoseley commented Mar 8, 2019

I can't remember anything about this, but XmlTextReader, like many of the major Xml types, is rich with virtual methods. Is it possible to make a more efficient one (dumping comments) by selectively overriding bits?

[edit] analogous to ProjectWriter : XmlTextWriter

@rainersigwald
Copy link
Member

@danmosemsft I hope so, and plan to look into that . . . after 16.0. I'm worried about changing that sort of thing at this point in the release cycle. Reactivated #2576 and assigned it to 16.1.

@rainersigwald
Copy link
Member

I'm going to merge this and prepare an insertion-to-VS PR. Don't let that stop anyone from describing a brilliant scoped fix that tweaks XmlReader behavior, should you come up with one!

rainersigwald added a commit to rainersigwald/msbuild that referenced this issue Mar 8, 2019
Works around a behavior difference between XmlReader and XmlTextReader
that caused newlines in attributes to be ignored in command-line builds.

Fixes dotnet#4210.
rainersigwald added a commit that referenced this issue Mar 8, 2019
@krwq
Copy link
Member

krwq commented Mar 8, 2019

Have you perhaps tried putting the script in the explicit CDATA i.e.:

<CommandToCreateFile>
<![CDATA[
your script goes here
]]>
</CommandToCreateFile>

(haven't tried but usually XML parsers treat it as a single blob and don't touch the content - this won't work directly in the attribute though)

Generally XML spec allows XML parsers for quite a bit of freedom for handling white spaces and especially new lines (perhaps too much) - I think the only reasonable option to force any behavior to be exactly as desired is do what @danmosemsft has suggested above. Note wrapping XmlReader/Write is quite wide-spread in the XML implementation so I believe that is the right way to go.

@rainersigwald
Copy link
Member

CDATA is a good idea but I don't think it helps here, since putting the command in a property and referencing the property already works around the attribute-newline difference.

This fix was approved for 16.0 and has been checked into an internal VS branch. If you see something like it and your MSBuild version is higher than 16.0.452, please yell loudly.

Thanks for reporting @bradking!

ladipro added a commit that referenced this issue Apr 27, 2021
Fixes #2576

### Context

The code already has support for parsing project files as read-only when we know that we're not going to be asked to write them back to disk. This flag is currently unused because the initial implementation in #3584 introduced a regression related to whitespace in attribute values (#4210).

### Changes Made

Trivially reverted part of #4213 that addressed the regression and added a hack to make `XmlReader` behave the same as `XmlTextReader`.

### Testing

Existing unit tests for correctness and ETW for performance. `XmlDocumentWithLocation.Load()` is ~26% faster with this change compared to baseline when building .NET Core projects. This translates to about 10 ms per one incremental CLI build of a Hello world application.
@AR-May AR-May added the triaged label Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants