Подтвердить что ты не робот

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

4b9b3361

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