MSBuild Item Include (подстановочный знак) не расширяется
Это очень странно. Мы пытались понять это на некоторое время, но это действительно не имеет никакого смысла.
Наш веб-проект импортирует файл целей, у которого есть цель, подобная этой:
<Target Name="CSSCheckInternal">
<ItemGroup>
<CSSFiles Include="$(MSBuildProjectDirectory)\**\*.css" />
</ItemGroup>
<CSSChecker Files="@(CSSFiles)" />
</Target>
В настоящий момент одна ветка строится идеально, выполняя задачу по желанию; но другая ветка не работает на указанной цели.
Сбой происходит из-за того, что элемент @(CSSFiles)
, полученный при выполнении задачи, не расширяется в массив ITaskItem
.
Задача записывается следующим образом (вплоть до того момента, когда я получаю метаданные FullPath):
public class CSSChecker : Task
{
[Required]
public ITaskItem[] Files
{
get;
set;
}
public override bool Execute()
{
string fullFilePath = null;
if (Files != null)
{
foreach (var item in Files)
{
fullFilePath = item.GetMetadata("FullPath");
if(!File.Exists(fullFilePath))
throw new InvalidOperationException(
string.Format("{0} does not exist", fullFilePath));
//rest of the code elided
Строка, которая не работает, бросает InvalidOperationException
на последнюю строку, например:
Файл не существует: C:\Code\Project\**\*. css
Таким образом, кажется, что MSBuild вместо расширения шаблона в атрибуте Include
просто передает строку поверх, создавая при этом только одну ITaskItem
.
На диске существует целевая папка , и единственное различие между сломанным файлом проекта и рабочим - это один файл, который гораздо раньше в файле проекта.
Update
I спросил Сайид Хашими на твиттере (написал книгу MSBuild), и благодаря этому попытался вынуть подстановочный файл **
и теперь он начал работать. Это не очень подходит, поскольку задача предназначена для повторного использования между проектами. Но, похоже, это как-то связано с этим.
Окончательное обновление
Пожалуйста, если кто-нибудь знает, в какой ситуации MSBuild не будет правильно расширять подстановочный знак, это будет большой помощью!
Ответы
Ответ 1
Я понял это - мне пришлось удалить папку obj\в моем каталоге проектов, и внезапно снова откроется подстановочный шаблон папки.
Короткий ответ для моей ситуации заключается в том, что появляется код обработки подстановочных знаков MSBuild, полностью удаленный, если какой-либо путь слишком длинный, и просто не создает группу элементов.
Что я здесь делаю, так как мне удалось создать пути, которые так долго? Ну, я этого не сделал. Это была встроенная задача публикации в сети - которую я использую так (для пользовательского развертывания, которое мы делаем):
<MSBuild Projects="$(Proj)" Properties="Platform=$(Platform);
Configuration=$(Configuration);DeployOnBuild=true;PackageAsSingleFile=False;
AutoParameterizationWebConfigConnectionStrings=False" />
Когда вы делаете PackageAsSingleFile=False
, который я использую, чтобы предотвратить создание zip, поскольку я хочу использовать развертываемые веб-сайты, в папке obj вы получаете такую структуру папок, как это:
[Project_Dir]\obj\[configuration]\Package\PackageTemp\[Project Dir]\[output *]
Если [Project_Dir] является c:\my project\
, тогда базовая папка для файлов временных пакетов будет выглядеть как c:\my project\obj\debug\Package\PackageTemp\c_c\my project\
.
Как вы можете видеть, эта довольно глубокая структура папок уже, и на самом деле проекты обычно не являются папками верхнего уровня в корне диска.
Я нашел в некоторых наших проектах, которые используют этот метод развертывания, становится невозможным удалить папку obj\
в проводнике или в командной строке, потому что путь слишком длинный. То, что я делаю, чтобы обойти это, - это переименовать столько родительских папок, сколько требуется, просто 1
, чтобы сократить полный путь и затем удалить. В предыдущем примере я переименовал бы следующее:
c:\my project\obj\1\1\1\1\1
Что хорошо работает.
Вы можете представить себе - если проект начинается в папке с достаточно большим объемом, то возможные пути элементов, сгенерированных для задачи публикации, будут очень длинными. Я обнаружил, что если я просто использую задачу Publish
из VS, это на самом деле вызывает ошибку во время публикации, но, похоже, что оболочка MSBuild в том виде, в котором я показываю выше, на самом деле каким-то образом обходит ограничение ограничения пути к папке. Я собираюсь собрать проект, который скоро докажет это.
Итак, в моем случае мне пришлось переписать мою задачу, чтобы взять базовые папки, которые должны быть обработаны, а затем перезапустить их через папки и файлы, игнорируя любую найденную папку "obj".
Я попробовал исключить любые файлы в папке obj с помощью атрибута 'Исключить", но это не имело никакого значения (предположительно потому, что оба были обрезаны!).
Ответ 2
У меня была такая же проблема, и я могу подтвердить, что она также связана с длинными путями. Мои были в папке node_modules, которой управляет npm.
Я обнаружил, что могу удалять длинные пути без необходимости переименовывать папки, как это делал Андреас, используя rimraf. Стоит просто повторить попытку, если вы получите какие-либо ошибки.
Кроме того, если на самом деле это не настоящая рекурсия, скажем, если вы используете синтаксис двойной звездочки (**) в качестве ярлыка для включения трех уровней папок, то вы можете заменить двойной синтаксис звездочки на серия выражений с использованием одиночных звездочек (*).
Например, если предположить, что на самом деле нужно включить только три уровня папок, вы можете поменять их...
C:\**\*.*
... для этого.
C:\*\*.*
C:\*\*\*.*
C:\*\*\*\*.*
Ответ 3
Эта проблема возникает, если какой-либо путь в рекурсивном поиске длиннее MAX_PATH
. Вы можете проверить это, пытаясь создать каталог в вашем самом глубоком пути с помощью проводника, вы получите сообщение об ошибке, указывающее, что путь слишком длинный, и каталог не может быть создан. Или, если вы хотите воспроизвести эту проблему, создайте путь, длина которого превышает максимальный путь, используя CreateFileW()
с расширенным синтаксисом пути \\\?\
.
MsBuild должен внутренне проверять длину до того, как он снова вызовет FindNextFile()
, так как вы никогда не видите слишком длинных ошибок в procmon. Что вы увидите, так это то, что на одном уровне до того, как путь слишком длинный, он просто получает ожидаемый ERROR_NO_MORE_FILES
, а затем закрывает ручку поиска, но даже не утруждает себя попыткой следующего уровня вниз. Как только это произойдет, MsBuild откажется от своего перечисления без ошибок!
Ответ 4
Неправильная ссылка файловой системы NTFS также приведет к такому поведению, например, с жесткой ссылкой во включенной папке, указывающей на несуществующий путь.