dotnet / msbuild

The Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.
https://docs.microsoft.com/visualstudio/msbuild/msbuild
MIT License
5.22k stars 1.35k forks source link

Can not evaluate project with preserveFormatting = true parameter #2772

Open xtmq opened 6 years ago

xtmq commented 6 years ago

Take a commit 87982abc4cbcd298a44fe164f2519b9fbcc10fde from https://github.com/dotnet/roslyn solution, run Restore.cmd script. Let's look at the project 'ROSLYN_DIR\src\Scripting\CSharp\CSharpScripting.csproj'.

I'm evaluating the project using the next code:

var projectRootElement = ProjectRootElement.Open(projectFilePath, projectCollection, false) var project = new Project(projectRootElement, properties, null, projectCollection, ProjectLoadSettings.Default)

Everything works. But now try to do the same with preserveFormatting = true option:

var projectRootElement = ProjectRootElement.Open(projectFilePath, projectCollection, true) var project = new Project(projectRootElement, properties, null, projectCollection, ProjectLoadSettings.Default)

MsBuild reports the next error:

The expression "[System.IO.Path]::Combine(C:\Temp\, .NETPortable,Version=v5.0,Profile= .AssemblyAttributes.cs)" cannot be evaluated. Illegal characters in path. C:\Program Files (x86)\Microsoft Visual Studio\Preview\Community\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets at (3223:5)

I guess the problem is in '\n' character in the end of 'Profile=' line. For me it looks like unexpected behavior (at least), because I just want to open, edit (add an item for example), and save the project, preserving user spaces and formatting.

Maybe this is wring API? And there is another one that I can use to edit project files without breaking formatting?

Environment data

msbuild /version output: Microsoft (R) Build Engine version 15.5.160.37545 for .NET Framework

OS info: Windows 10 Pro 1703, 15063.726

zvirja commented 6 years ago

@rainersigwald I saw your activities around - could advice who can be invited to this issue to take a closer look? Thanks a lot in advance! 🙏

mfilippov commented 6 years ago

@AndyGerlicher @cdmihai @rainersigwald Have you any ideas about this issue?

AndyGerlicher commented 6 years ago

Not sure what the actual issue was, but it's fixed in 15.8. I was able to repro that exception in 15.7 on the Roslyn repo file, but 15.8 evaluated it just fine. Feel free to re-open if there's still an issue. Because this was a fun use of MSBuildLocator I'll include the source to my repro:

public class Runner
{
    public void Run(string projectFilePath, bool preserveFormatting)
    {
        var projectCollection = new ProjectCollection();
        var properties = new Dictionary<string, string>();

        var projectRootElement = ProjectRootElement.Open(projectFilePath, projectCollection, preserveFormatting);
        var project = new Project(projectRootElement, properties, null, projectCollection, ProjectLoadSettings.Default);
        Console.WriteLine("Success");
    }
}

public class Program
{
    static void Main(string[] args)
    {
        if (args.Length < 2) Environment.Exit(1);

        var instances = MSBuildLocator.QueryVisualStudioInstances().ToList();
        var msbuildDeploymentToUse = AskWhichMSBuildToUse(instances);

        if (msbuildDeploymentToUse.VSInstance != null)
        {
            Console.WriteLine(
                $"Using MSBuild from VS Instance: {msbuildDeploymentToUse.VSInstance.Name} - {msbuildDeploymentToUse.VSInstance.Version}");
            Console.WriteLine();

            MSBuildLocator.RegisterInstance(msbuildDeploymentToUse.VSInstance);
        }
        else
        {
            Console.WriteLine($"Using MSBuild from path: {msbuildDeploymentToUse.MSBuildPath}");
            Console.WriteLine();

            MSBuildLocator.RegisterMSBuildPath(msbuildDeploymentToUse.MSBuildPath);
        }

        var projectFilePath = args[0];
        var preserveFormatting = bool.Parse(args[1]);
        new Runner().Run(projectFilePath, preserveFormatting);
    }

    // Copy/paste from https://github.com/Microsoft/MSBuildLocator/blob/master/samples/BuilderApp/Program.cs#L54-L108
    private static (VisualStudioInstance VSInstance, string MSBuildPath) AskWhichMSBuildToUse(List<VisualStudioInstance> instances)
    {
        if (instances.Count == 0)
        {
            Console.WriteLine("No Visual Studio instances found!");
        }

        Console.WriteLine($"0) Custom path");
        for (var i = 1; i <= instances.Count; i++)
        {
            var instance = instances[i - 1];
            var recommended = string.Empty;

            // The dev console is probably always the right choice because the user explicitly opened
            // one associated with a Visual Studio install. It will always be first in the list.
            if (instance.DiscoveryType == DiscoveryType.DeveloperConsole)
                recommended = " (Recommended!)";

            Console.WriteLine($"{i}) {instance.Name} - {instance.Version}{recommended}");
        }

        Console.WriteLine();
        Console.WriteLine("Select an instance of MSBuild: ");
        var answer = Console.ReadLine();

        if (int.TryParse(answer, out int instanceChoice) && instanceChoice >= 0 &&
            instanceChoice <= instances.Count)
        {
            if (instanceChoice == 0)
            {
                Console.WriteLine("Input path to MSBuild deployment:");
                var msbuildPath = Console.ReadLine();

                if (!Directory.Exists(msbuildPath))
                {
                    Console.WriteLine($"Directory does not exist: {msbuildPath}");
                    Environment.Exit(-1);
                }

                return (null, msbuildPath);

            }
            else
            {
                var instanceUsed = instances[instanceChoice - 1];
                return (instanceUsed, null);
            }
        }
        else
        {
            Console.WriteLine($"{answer} is not a valid response.");
            Environment.Exit(-1);
        }

        throw new Exception("Invalid parsing");
    }
}

Output

C:\>EvaluateProject.exe d:\src\roslyn\src\Scripting\CSharp\CSharpScripting.csproj true
0) Custom path
1) DEVCONSOLE - 15.8.0 (Recommended!)
2) Visual Studio Enterprise 2017 - 15.8.27916.1
3) Visual Studio Enterprise 2017 - 15.7.27703.2042

Select an instance of MSBuild:
2
Using MSBuild from VS Instance: Visual Studio Enterprise 2017 - 15.8.27916.1

Success

C:\>EvaluateProject.exe d:\src\roslyn\src\Scripting\CSharp\CSharpScripting.csproj false
0) Custom path
1) DEVCONSOLE - 15.8.0 (Recommended!)
2) Visual Studio Enterprise 2017 - 15.8.27916.1
3) Visual Studio Enterprise 2017 - 15.7.27703.2042

Select an instance of MSBuild:
2
Using MSBuild from VS Instance: Visual Studio Enterprise 2017 - 15.8.27916.1

Success

The failure on 15.7:

C:\>EvaluateProject.exe d:\src\roslyn\src\Scripting\CSharp\CSharpScripting.csproj true
0) Custom path
1) DEVCONSOLE - 15.8.0 (Recommended!)
2) Visual Studio Enterprise 2017 - 15.8.27916.1
3) Visual Studio Enterprise 2017 - 15.7.27703.2042

Select an instance of MSBuild:
3
Using MSBuild from VS Instance: Visual Studio Enterprise 2017 - 15.7.27703.2042

Unhandled Exception: Microsoft.Build.Exceptions.InvalidProjectFileException: The expression "[System.IO.Path]::Combine(C:\Users\angerlic\AppData\Local\Temp\, .NETPortable,Version=v5.0,Profile=
    .AssemblyAttributes.cs)" cannot be evaluated. Illegal characters in path.  C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args)
   at Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject[T1,T2](IElementLocation elementLocation, String resourceName, T1 arg0, T2 arg1)
   at Microsoft.Build.Evaluation.Expander`2.Function`1.Execute(Object objectInstance, IPropertyProvider`1 properties, ExpanderOptions options, IElementLocation elementLocation)
   at Microsoft.Build.Evaluation.Expander`2.PropertyExpander`1.ExpandPropertyBody(String propertyBody, Object propertyValue, IPropertyProvider`1 properties, ExpanderOptions options, IElementLocation elementLocation, UsedUninitializedProperties usedUninitializedProperties)
   at Microsoft.Build.Evaluation.Expander`2.PropertyExpander`1.ExpandPropertiesLeaveTypedAndEscaped(String expression, IPropertyProvider`1 properties, ExpanderOptions options, IElementLocation elementLocation, UsedUninitializedProperties usedUninitializedProperties)
   at Microsoft.Build.Evaluation.Expander`2.PropertyExpander`1.ExpandPropertiesLeaveEscaped(String expression, IPropertyProvider`1 properties, ExpanderOptions options, IElementLocation elementLocation, UsedUninitializedProperties usedUninitializedProperties)
   at Microsoft.Build.Evaluation.Expander`2.ExpandIntoStringLeaveEscaped(String expression, ExpanderOptions options, IElementLocation elementLocation)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluatePropertyElement(ProjectPropertyElement propertyElement)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluatePropertyGroupElement(ProjectPropertyGroupElement propertyGroupElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   at Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   at Microsoft.Build.Evaluation.Evaluator`4.Evaluate(ILoggingService loggingService, BuildEventContext buildEventContext)
   at Microsoft.Build.Evaluation.Project.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings)
   at Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings)
   at Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   at Microsoft.Build.Evaluation.Project..ctor(ProjectRootElement xml, IDictionary`2 globalProperties, String toolsVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings)
   at EvaluateProject.Runner.Run(String projectFilePath, Boolean preserveFormatting) in C:\Users\angerlic\source\repos\EvaluateProject\EvaluateProject\Program.cs:line 113
   at EvaluateProject.Program.Main(String[] args) in C:\Users\angerlic\source\repos\EvaluateProject\EvaluateProject\Program.cs:line 43
xtmq commented 6 years ago

Thanks a lot!

xtmq commented 5 years ago

Hi @AndyGerlicher, issue is still there =( When I set preserveFormatting = true MsBuild does not evaluate project, see the example below:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Suffix>
    </Suffix>
    <TargetName>MyFile$(Suffix).exe</TargetName>
    <TargetPath>$([System.IO.Path]::Combine($(ProjectDir),$(TargetName)))</TargetPath>
  </PropertyGroup>
</Project>

Error message:

The expression "[System.IO.Path]::Combine('', MyFile
    .exe)" cannot be evaluated. Illegal characters in path.  C:\Work\TestProject.proj at (7:5)

Without preserveFormatting everything works just fine.

MsBuild version: 15.9.21+g9802d43bc3

xtmq commented 5 years ago

@AndyGerlicher could you reopen the issue?

cdmihai commented 5 years ago

@rainersigwald @livarcocc

mfilippov commented 5 years ago

Any news about this issue?

livarcocc commented 5 years ago

This is not something that has bubbled up to the top of our list yet. Sorry.

mkress commented 4 years ago

bubble up? :-) I have the same error...

haolly commented 2 years ago

Is any news about this issue? since the last comment is two years ago