phmonte / Buildalyzer

A utility to perform design-time builds of .NET projects without having to think too hard about it.
MIT License
589 stars 92 forks source link

Wrong "OutputPath" and "OutDir"? #205

Open mcreek opened 2 years ago

mcreek commented 2 years ago

With Buildalyzer (version 4.1.3) I'm analyzing CSPROJ files which are part of a solution file. For build-output configuration I'm using one common "common.props" file instead of configuring it at all csproj files individually.

Example of file "common.props"

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        ...
        <OutputPath>$(SolutionDir)\build\bin\$(Configuration)\</OutputPath>
        ...
    </PropertyGroup>
</Project>

This "common.props" file is included in each csproj file with

<Import Project="..\..\Common.props" />

and the "OutputPath" Property is removed in each csproj file .

My directory structure is:

SampleApp
|--SampleApp.sln
|--common.props
|
|--build
|   |--bin
|       |--debug
|       |--release
|
|--src
    |--SampleAppProj_1
    |   |--SampleAppProj_1.csproj
    |   |-- ...
    |
    |--SampleAppProj_2
        |--SampleAppProj_1.csproj
        |-- ...

Analyzing the projects with code:

public void Test(string projectFile)
{
    AnalyzerManager manager = new AnalyzerManager();
    IProjectAnalyzer analyzer = manager.GetProject(projectFile);
    analyzer.SetGlobalProperty("Configuration", "debug");

    string tempDir = Path.Combine(Path.GetDirectoryName(projectFile), "Temp") + Path.DirectorySeparatorChar;
    EnvironmentOptions options = new EnvironmentOptions();
    options.GlobalProperties["IntermediateOutputPath"] = tempDir; //designation of intermediate output file directory
    options.TargetsToBuild.Remove("Clean"); //see https://github.com/daveaglick/Buildalyzer/issues/105)

    IAnalyzerResults results = analyzer.Build(options);
    IAnalyzerResult analyzerResult = results.First();

    System.Diagnostics.Debug.WriteLine(analyzerResult.GetProperty("OutputPath"));
}

This will print out for "OutputPath": "\<drive>\SampleApp\src\SampleAppProj_1\\build\bin\debug\" instead of "\<drive>\SampleApp\build\bin\debug\" Same for "OutDir" property.

I'am doing something wrong? How can I get the correct "OutputPath" and "OutDir"?

daveaglick commented 2 years ago

Buildalyzer doesn't actually process the project file on its own (at least not beyond very rudimentary parsing to figure out how to call MSBuild), and the values in IAnalyzerResult come directly from MSBuild logging. So that's all to say that if the OutputPath and OutDir props aren't coming over correctly, then it's because MSBuild isn't resolving them as you expect when Buildalyzer calls it.

There's a few reasons why this could be:

The best way to diagnose what's going on here is probably to log what Buildalyzer is doing and then do it yourself from the command line. For example:

StringWriter log = new StringWriter();
AnalyzerManager manager = new AnalyzerManager(
    new AnalyzerManagerOptions
    {
        LogWriter = log
    });
// ...
System.Diagnostics.Debug.WriteLine(log.ToString());

That'll let you see exactly what command line Buildalyzer ran and from where and then you can run it yourself and compare.

The next step after that would probably be to generate a binary log and open it with the Structured Log Viewer. That should let you see exactly what's going on under the hood, how and why those output values are being set the way they are, and where or if the common.props file is being processed:

IProjectAnalyzer analyzer = manager.GetProject(projectFile);
analyzer .AddBinaryLogger("myproject.binlog")));

To really get down to what the differences are, use the Project System Tools extension to generate a binary log file from Visual Studio too and compare the two.

mcreek commented 2 years ago

Thank you for response and the description how to analyze it. I will try to analyze it but I will need some time for it.

Bertk commented 5 months ago

Please use Directory.Build.props and Directory.Build.targets instead of "common.props".

OutputPath property is a relativ path and BaseOutputPathshould be used.

image image

see also .NET 8.0 SDK Artifacts output layout

phmonte commented 3 months ago

@mcreek, Do you have any updates on this?

Bertk commented 3 months ago

This might be helpful. OutputPath vs OutDir