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 также приведет к такому поведению, например, с жесткой ссылкой во включенной папке, указывающей на несуществующий путь.