Вместо того, чтобы строить структуру папок по умолчанию, которая похожа на

Solution.sln
Project1
    bin    <- project 1 output
    obj    <- project 1 intermediate output
Project2
    bin    <- project 2 output
    obj    <- project 2 intermediate output

Вместо этого я хочу построить это как

Solution.sln
bin    <- project 1 AND 2 output
obj
    Project1   <- project 1 intermediate output
    Project2   <- project 2 intermediate output

Я могу сделать

msbuild "/p:OutputPath=../bin" "/p:IntermediateOutputPath=../obj/" Test123.sln

Однако использование "/p:IntermediateOutputPath=../obj/$(ProjectName)/" не работает. Вместо того, чтобы создавать папку для каждого проекта, он создает одну папку, буквально называемую $(ProjectName) (я читал это больше всего, но не все эти макросы на самом деле являются вещью Visual Studio, а не магией MSBuild).

Как я могу использовать значение для конкретного проекта (например, ProjectName) в значении свойства (например, IntermediateOutputPath) при построении?

(Некоторая справочная информация:

Наличие одной папки bin на уровне решения избавляет от ненужного копирования выходных файлов, которые быстро накапливают более 100 МБ в больших решениях. Кроме того, он сохраняет исходные папки в чистоте, поэтому они могут быть доступны только для чтения.

Однако мне все еще нужны отдельные папки obj, потому что кто знает, что там происходит - могут быть одинаковые имена файлов для разных проектов.)

0
dialer 20 Апр 2018 в 22:31

1 ответ

Лучший ответ

Вы можете переопределить CustomAfterMicrosoftCommonTargets объекта Microsoft.Common.targets. Он позволяет вводить настраиваемые цели в проекты и выполнять некоторые действия.


Точка входа в процесс сборки - Make.targets:

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; DefaultTargets="EntryMSBuild">

    <ItemGroup>
        <Project Include="**\*.csproj"/>
    </ItemGroup>

    <Target Name="EntryMSBuild">

        <Message Text="-----Entry-----" Importance="high"/>
        <Message Text="    Rebuild    " Importance="high"/>
        <Message Text="-----Entry-----" Importance="high"/>

        <MSBuild Projects="@(Project)" Targets="rebuild" Properties="CustomBeforeMicrosoftCommonTargets=$(MSBuildThisFileDirectory)Setting.targets"/>
    </Target>

</Project>

В Setting.targets определены IntermediateOutputPath и OutputPath для каждого проекта:

<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
        <IntermediateOutputPath>..\Global\obj\$(MSBuildProjectName)</IntermediateOutputPath>
    </PropertyGroup>

    <PropertyGroup>
        <OutputPath>..\Global\bin\</OutputPath>
    </PropertyGroup>

</Project>
  • IntermediateOutputPath - зависит от $ (MSBuildProjectName);
  • OutputPath - общий для всех проектов.

В результате вы получите желаемую структуру:

Solution.sln
bin    <- project 1 AND 2 output
obj
    Project1   <- project 1 intermediate output
    Project2   <- project 2 intermediate output

Протестировано на решении с двумя проектами под .net46 и .netstandard2.0. MSBuild 15.6.82.30579


Вам не нужно хранить пользовательские цели в C:\Program Files[(x86)]\microsoft visual studio\2017\xxx\msbuild\15.0 или любом заранее определенном пути. Вы переопределяете свойство CustomBeforeMicrosoftCommonTargets, которое уже определено в Microsoft.Common.targets и внедрено в проект по умолчанию.

Из комментария Microsoft.Common.targets:

Этот файл определяет шаги стандартного процесса сборки для проектов .NET. Он содержит все шаги, которые являются общими для разных языков .NET, таких как Visual Basic и Visual C #.

2
Peter Mortensen 4 Авг 2020 в 00:30