Используйте $(SolutionName) в параметрах командной строки MsBuild
Чтобы эмулировать параметр "PerProject" в сборке XAML TFS 2013 в новых сборках на основе задач Build 2015, я хотел бы иметь возможность передавать SolutionName в аргументы командной строки msbuild без необходимости каждый раз устанавливать его вручную.
Я хотел бы сделать что-то вроде:
/p:OutputPath=$(Build.BinariesDirectory)\$(SolutionName)\
Где я бы хотел, чтобы MsBuild определял параметр $(SolutionName). Но при передаче этого в командной строке новый исполнитель задач заменит $(Build.BinariesDirectory)
с правильным целевым путем и листьями $(SolutionName)
в одиночестве. К сожалению, MsBuild впоследствии также оставляет собственность одну:
Copying file from "obj\Debug\TFSBuild.exe" to "bin\debug\$(SolutionName)\TFSBuild.exe".
TFSBuild -> b\$(SolutionName)\TFSBuild.exe
Copying file from "obj\Debug\TFSBuild.pdb" to "b\$(SolutionName)\TFSBuild.pdb".
Я не могу вспомнить, как передать свойство в командную строку и сделать ли это позднее расширение... Любые советы?
Для тех, кто хочет подражать SingleFolder
или же AsConfigured
это легко:
SingleFolder -> /p:OutputPath="$(Build.BinariesDirectory)"
Asconfigured -> don't pass OutputPath
PerProject -> /p:OutputPath="$(Build.BinariesDirectory)\HARDCODESOLUTIONNAME"
3 ответа
Как я и опасался, похоже, не существует простого способа переопределить свойство из командной строки и "внедрить" в него значение другого свойства на этапе оценки.
Есть несколько способов обойти это, но они не идеальны и, конечно, не универсальны для каждого языка, поддерживаемого MsBuild. Жалость.
Я отладил целевые файлы MsBuild и нашел решение для воспроизведения старого поведения эпохи 2005/2008. Не полностью для решения, но оно перенаправляет проекты в подпапку.
/p:GenerateProjectSpecificOutputFolder=true /p:OutDirWasSpecified=true
/p:OutputPath=$(Build.BinariesDirectory)
Как обычно, $(SolutionName)
определяется при выполнении конвейеров MSBuild уровня решения, таких как запуск dotnet restore
в корневом каталоге решения.
Делать $(SolutionName)
доступно для конвейеров MSBuild на уровне проекта, добавьте Directory.Build.props
файл в корне вашего решения со следующим содержимым:
<Project>
<PropertyGroup>
<SolutionName Condition="'$(SolutionName)' == ''">
$([System.IO.Path]::GetFileNameWithoutExtension($([System.IO.Directory]::GetFiles("$(MSBuildThisFileDirectory)", "*.sln")[0])))
</SolutionName>
</PropertyGroup>
</Project>
Сейчас же $(SolutionName)
будет определен даже при выполнении конвейеров MSBuild на уровне проекта.
Этот ответ работает лучше всего, когда в корне каталога решений есть ровно один файл решения. Вам нужно будет немного помассировать вышесказанное для других структур проекта.
Конечно, вы также можете полениться и указать имя решения напрямую, но это открывает возможность проблем с рефакторингом (не забудьте обновить этот файл, если имя решения изменится).
<Project>
<PropertyGroup>
<SolutionName Condition="'$(SolutionName)' == ''">
MySolutionName
</SolutionName>
</PropertyGroup>
</Project>
Одно из решений состоит в том, чтобы имитировать такую "позднюю оценку" самостоятельно, изменяя OutputPath в рамках файла проекта. Чтобы обойтись без изменения каждого отдельного файла проекта, вы можете использовать CustomBeforeMicrosoftCSharpTargets
точка расширения. Что является причудливым способом сказать, что это просто свойство, которое, когда оно найдено и указывает на существующий файл, приведет к тому, что этот файл будет импортирован куда-то раньше всей фактической логики сборки. Вот идея: создайте где-нибудь файл типа paths.targets - либо включите его в систему контроля версий, либо вы можете сгенерировать его на лету как часть процесса сборки. Содержание:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath Condition="'$(OutputPathBaseDir)'!=''">$(OutputPathBaseDir)\$(SolutionName)</OutputPath>
</PropertyGroup>
</Project>
Так что это просто переопределяет OutputPath к некоторому базовому dir + имя решения. Тогда, если вы строите решение, как
msbuild my.sln /p:CustomBeforeMicrosoftCSharpTargets=paths.targets;
OutputPathBaseDir=$(Build.BinariesDirectory)
каждый проект импортирует файл paths.targets и устанавливает выходное свойство в valueOfBinariesDirectory\my, которое, я думаю, именно то, что вам нужно.
Вы правы, что сборка TFS vNext не может распознать $(SolutionName)
в OutputPath, поскольку $(SolutionName) не отображается в предопределенных переменных.
В качестве альтернативы мы можем назвать определение сборки именем решения, а затем указать аргумент MSBuild: /p:OutputPath="$(Build.BinariesDirectory)\$(Build.DefinitionName)"
таким образом, мы можем получить вывод под именем решения.