Использование MSBuild для создания нескольких конфигураций

Я пытаюсь отредактировать файл своего проекта, чтобы у меня был проект, который строит сразу несколько конфигураций сборки. Я сделал это с использованием пакетного подхода и задачи MSBuild (см. Ниже).

Если я запускаю скрипт, я получаю такую ​​ошибку:

Ошибка 103 Свойство OutputPath не задано для проекта ThisMSBuildProjectFile.csproj. Убедитесь, что вы указали допустимую комбинацию конфигурации и платформы для этого проекта. Конфигурация = «Отладка» Платформа = «AnyCPU».

Я получаю это, если добавляю или опускаю OutputPath из задачи MSBuild. Если для пошагового выполнения сценария используется отладчик VS2010 и вызывается задача MSBuild - отладчик снова входит в файл, а затем входит в OutputPath, так что, черт возьми, он должен выбрать это значение, нет?

Любая помощь в этом будет принята с благодарностью - это сводит меня с ума. Спасибо, Пол.

ThisMSBuildProjectFile.csproj (излишки удалены):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">

  <!-- Only Import normal targets if not building multiple projects -->
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition="'$(Configuration)|$(Platform)' != 'AllBuild|AnyCPU' "/>

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == '' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>C:\Folder\Etc\Output\$(Configuration)\</OutputPath>
    <OutDir>C:\Folder\Etc\Output\$(Configuration)\</OutDir>
    <BaseOutputPath>C:\Folder\Etc\Output\$(Configuration)\</BaseOutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>

  <!-- Common -->
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <Platform>AnyCPU</Platform>
    <!-- Repeated properties from above here (including, of course, OutputPath) -->  
   </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <!-- Repeated properties from above here (including, of course, OutputPath) --> 
  </PropertyGroup>

  <ItemGroup>
    <Projects Include="C:\Folder\Etc\ThisMSBuildProjectFile.csproj" />
  </ItemGroup>

   <!-- Call this project file again, but with a different configuration - if this was working, this would call multiple  build configs -->
  <Target Name="Build" Condition="'$(Configuration)|$(Platform)' == 'AllBuild|AnyCPU' ">
    <Message Text="hm!"/>
    <!-- Tried thiswith and without the OutputPath property - makes no difference. -->
   <MSBuild  Projects="@(Projects)" Properties="Configuration=Debug;OutputPath=C:\Folder\Etc\Output\" ToolsVersion="4.0" Condition="'$(Configuration)|$(Platform)' == 'AllBuild|AnyCPU' "/>
 </Target>

   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AllBuild|AnyCPU' ">
    <!-- Repeated properties from above here (including, of course, OutputPath) --> 
  </PropertyGroup>

  <!-- Project files -->
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Blah\Blah.cs" />
  </ItemGroup>


person p.q    schedule 01.04.2011    source источник


Ответы (3)


Важно понимать, что при использовании задачи «MSBuild» будет запущен новый дочерний процесс MSBuild. Это означает, что любые элементы и свойства, которые вы определяете в родительском процессе MSBuild, не будут автоматически передаваться / отображаться из дочернего процесса MSBuild , если вы явно не передадите их через Атрибут Properties в элементе MSBuild (как в <MSbuild Properties="..." />).

Чтобы ответить на ваш вопрос, я написал следующий автономный пример, который запускает дочерний проект MSBuild для всех указанных конфигураций:

  1. Сначала создайте каталог для эксперимента MSBuild (например, я использовал C:\temp\msbuildtest)

  2. В этом каталоге создайте первый файл main.proj:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="4.0">
        <ItemGroup>
            <ConfigList Condition=" '@(ConfigList)' == '' and $(Config) != '' " Include="$(Config.Split('+'))" /><!-- parse all requested configurations into a list -->
            <ConfigList Condition=" '@(ConfigList)' == '' " Include="Debug" /><!-- if no configurations were specified, default to Debug -->
        </ItemGroup>
        <!--
    
        Build the child project for each requested configuration. -->
        <Target Name="Build">
            <MSBuild Projects="$(MSBuildProjectDirectory)\child.proj" Properties="Configuration=%(ConfigList.Identity);OutputPath=$(MSBuildProjectDirectory)\bin\%(ConfigList.Identity)" Targets="Build" />
        </Target>
    </Project>
    
  3. В том же каталоге создайте второй файл child.proj (в вашем случае это будет реальный проект C #, который вы пытаетесь создать, но поскольку я пытаюсь проиллюстрировать свою точку зрения, я использую простой дочерний проект, который вместо при запуске компилятора C # просто выводятся значения свойств :-))

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="4.0">
        <Target Name="Build">
            <Message Text="Building configuration $(Configuration) with output path $(OutputPath)" Importance="High" />
        </Target>
    </Project>
    
  4. Теперь вы можете запустить пример. Сначала по умолчанию, если вы явно не указываете конфигурации для сборки:

    C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild main.proj
    > (cut the noise)
    > Build:
    >   Building configuration Debug with output path C:\temp_c\d\bin\Debug
    

    А затем явно указали несколько конфигураций:

    C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild main.proj /property:Config=Debug+Release+Staging+Production
    > (cut the noise)
    > Build:
    >   Building configuration Debug with output path C:\temp_c\d\bin\Debug
    > Build:
    >   Building configuration Release with output path C:\temp_c\d\bin\Release
    > Build:
    >   Building configuration Staging with output path C:\temp_c\d\bin\Staging
    > Build:
    >   Building configuration Production with output path C:\temp_c\d\bin\Production
    

Вы должны уметь адаптировать эту технику к своей ситуации.

person Milan Gardian    schedule 01.04.2011
comment
Отлично, спасибо за это - это не совсем то, что я искал (поскольку я хотел минимизировать изменения в стандартных файлах CSProj), но похоже, что я слишком много думал. Я пока буду придерживаться этого подхода. Спасибо - person p.q; 01.04.2011
comment
К сведению, этот метод называется пакетной обработкой задач. Существует множество способов пакетной обработки, и Сайед Ибрагим Хашими описывает их в своей книге «Внутри Microsoft Build Engine: Использование MSBuild и Team Foundation Build». - person Jon Schoning; 26.11.2012
comment
Важно понимать, что при использовании задачи MSBuild будет запущен новый дочерний процесс MSBuild. Это неправда. Согласно msdn.microsoft.com/en-us/library/z7f65y0d.aspx: в отличие от использования задачи Exec для запуска MSBuild.exe, эта задача использует тот же процесс MSBuild для создания дочерних проектов. Это также соответствует моему собственному опыту. - person Daniel Yankowsky; 07.12.2012
comment
@MilianGardian есть разница между And и и? - person Radu; 25.11.2015
comment
@Radu Я считаю, что логические операторы нечувствительны к регистру, но я никогда не пробовал варианты в верхнем регистре :-). - person Milan Gardian; 26.11.2015

Что-то не так в вашем файле проекта. Рассмотрим этот XML:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == '' ">
  <DebugType>pdbonly</DebugType>
  <Optimize>true</Optimize>
  <OutputPath>C:\Folder\Etc\Output\$(Configuration)\</OutputPath> 
  ...
</PropertyGroup>

Эти свойства никогда не могут быть установлены, поскольку даже если $ (Configuration) и $ (Platform) пусты, они никогда не могут соответствовать пустой строке при объединении с символом полосы; минимальное значение для этого условия - "|" и не ''. Даже если это исправлено путем сравнения условия с "|", вы затем продолжаете пытаться использовать $ (Configuration) в OutputPath в этой PropertyGroup, но $ (Configuration) никогда не будет иметь значение в момент его использования. Точно так же, когда вы пытаетесь установить для $ (Platform) значение «AnyCPU», оно уже должно иметь это значение. Вероятно, вы хотели вообще опустить условие в первой группе свойств, и вам может потребоваться указать значения по умолчанию для $ (Configuration) и $ (Platform) в ранней группе PropertyGroup без каких-либо условий. Сравните весь свой проект с новым и посмотрите, есть ли в нем еще какие-нибудь странности, подобные этому.

Также обратите внимание, что при переопределении цели «Build» у вас появляется избыточное условие для задачи MSBuild; при таком же условии он вам не понадобится ни для одной из задач.

person Brian Kretzler    schedule 01.04.2011
comment
Конечно, причина в том, что он взят из стандартного файла CSProj - и причина, по которой он существует в качестве запасного варианта, если конфигурация не указана. Я понимаю, что в приведенном мною примере этого не произошло, но при использовании отладчика MSBuild другие условные группы были задействованы, как и ожидалось. Спасибо, что указали на дублирование дочерних узлов! - person p.q; 01.04.2011
comment
Возможно, вы упускаете мою точку зрения, это вовсе не запасной вариант, при такой кодировке условие никогда не будет оцениваться как истинное, потому что левая часть всегда будет содержать как минимум '|' а правая часть всегда будет пустой, они никогда не могут быть равны. - person Brian Kretzler; 01.04.2011

Я не совсем уверен, захочу ли я пройти через такую ​​запутанную конфигурацию самого файла csproj проекта. Я бы предпочел создать отдельный файл MSBuild «BuildBoth.proj», который имеет конкретную цель под названием «Оба», которая создает решение в обеих конфигурациях.

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

    <!-- Calls twice for both configs -->
    <Target Name="Both">
        <MSBuild Projects="buildboth.sln" Targets="Rebuild" Properties="Configuration=Debug"
                         StopOnFirstFailure="true">
        </MSBuild>

        <MSBuild Projects="buildboth.sln" Targets="Rebuild" Properties="Configuration=Release"
                         StopOnFirstFailure="true">
        </MSBuild>
    </Target>

    <!-- single config targets

    <Target Name="Debug">
        <MSBuild Projects="buildboth.sln" Targets="Rebuild" Properties="Configuration=Debug"
                         StopOnFirstFailure="true">
        </MSBuild>
    </Target>

    <Target Name="Release">
        <MSBuild Projects="buildboth.sln" Targets="Rebuild" Properties="Configuration=Release"
                         StopOnFirstFailure="true">
        </MSBuild>
    </Target>
    -->

</Project>

Затем я бы запустил команду (уровень детализации установлен минимальный), чтобы настроить таргетинг на оба.

C:\Projects\experiments\BuildBoth>msbuild /v:m /target:Both BuildBoth.proj
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.225]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

  BothWpf -> C:\Projects\experiments\BuildBoth\BothWpf\bin\Debug\BothWpf.exe
  BothWpf -> C:\Projects\experiments\BuildBoth\BothWpf\bin\Release\BothWpf.exe
person icelava    schedule 01.04.2011
comment
Привет - спасибо, я понимаю, что у меня может быть отдельный проект, но идея заключалась в том, чтобы члены команды могли отправлять сборки из VS, а не выкладывать их - я хотел минимально изменить стандартный файл CSProj, чтобы включить остальные команды, чтобы сделать это. Спасибо за ответ. - person p.q; 01.04.2011