Comments 37
Статья однозначно хорошая, не спорю.
А вы используете MSBuild файлы в качестве основных?
Если серьезно, то на MSBuild живет много чистых windows-проектов которым не нужна кросс-платформенность. Да и интеграция GUI в Студии с MSBuild остается по-прежнему лучше.
Виндовый мелко-мягкий адище!
С другой стороны — это как завести козу ;)
Вообще, лично я перестал использовать студию для генерации проектов. У меня все эти *.sln, *.vcxproj, etc — попадают в игнор и удаляются во время clean. Хоть и разработка идёт только под винду, всё равно намного удобнее сгенерить файлы проектов под кокретную версию VS.
грусть-печаль.
А в чём проблема прописать Condition="'$(Configuration)' == 'Debug'"
?
Так вроде же через IDE настраиваемая конфигурация выбирается сверху-слева в диалоге?
а вручную сработало:
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Link>
<AdditionalDependencies>zlibstatic.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
Некрокоммент, но может кому-то будет полезно.
1) Если не хочется заморачиваться с импортами своих .props
и .targets
файлов в каждый проект, то можно создать файл Directory.Build.props
и/или Directory.Build.targets
, которые импортируются автоматически.
2) Лучше избегать $(SolutionDir)
, потому что он существует только если солюшен строится из Студии. На билд-сервере ваши проекты будут строиться голым MSBuild, который использует .sln
только чтобы получить список проектов и билд-завсимостей, после чего каждый проект строится изолировано от солюшена. Если надо привязаться к стабильному пути, то лучше использовать $(MSBuildThisFileDirectory)
, который равен пути к самому props/targets
-файлу. Если такой файл лежит рядом c .sln
, то это даст надёжный переносимый эквивалент $(SolutionDir)
.
3) Бывает так, что солюшен не плоский (все проекты в одной папке), а иерархическй, т.е. проекты разбиты по поддиректориям (например, пачка проектов в поддиректории Core
, другая пака в поддиректории Client
, третья — в поддиректории Server
и т.д), и в таком случае на каждом уровне иерархии могут появляться собственные настройки, в дополнение к общим. Например, все проекты в Client
подключают один набор заголовочный файлов и библиотек и используют один набор параметров, все проекты в Server
— другой набор, и при этом все проекты вообще должны использовать общий solution-wide набор. В таком сценарии можно использовать комбо из приёмов 1 и 2, положив на каждом уровне иерархии по файлику Directory.Build.*
примерно такого вида:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Импортируем вышележащий Directory.Build.props, если таковой есть. В самом верхнем файле этого можно не добавлять. -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<!-- Для удобства делаем короткий алиас для $(MSBuildThisFileDirectory) -->
<PropertyGroup>
<_ThisDir>$(MSBuildThisFileDirectory)</_ThisDir>
</PropertyGroup>
<!-- Добавляем параметры, общие для всех подпроектов в данной иерархии -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(_ThisDir)Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ResourceCompile>
<AdditionalIncludeDirectories>$(_ThisDir)Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
...
</ItemDefinitionGroup>
</Project>
Они подключатся автоматически, и все параметры протянутся по иерархии сверху вниз, от корня к каждому подпроекту.
4) Лучше не использовать дефолтные $(OutDir)
и $(IntDir)
, которые по умолчанию гадят прямо в папку проекта (in-source build), и приучиться всегда строить во внешнюю папку (out-of-source build), которая находится за пределами солюшена, и может даже за пределами репозитория.
Во-первых, меньше шансов случайно закоммитить мусор в source control; проще настроить .gitignore
.
Во-вторых, облегчается поиск по исходникам без открытия студии (из Far-а, например) — не нужен фильтр для исключеня .exe;.obj;.pch;...
.
В-третьих, можно быстро сделать clean и освободить кучу места на диске, не открывая студии — достаточно удалить одну-единственную директорию, а не лазить по всему дереву проекта, удаляя папки /Debug, /Release, /obj, /bin, /generated и т.п.
В-четвёртых, на билд-сервере проекты часто билдаются именно out-of-source, валя всё в одну папку, и иногда это тихо ломает ваш in-source билд. Например, два проекта зависят от одной DLL, копируя их каждый в свой output. Но вот версии незаметно разбежались. Пока строим локально из студии, каждый проект получает свою версию, и работает нормально, разработчик считает, что всё отлично. Но когда делатся "сводный" билд в общую папку на билд-сервере, то одна версия может затереть другую, недетерминированным способом (в зависимости от порядка и времени билда каждого проекта), и это может долго оставаться незамеченным. Если делать "сводные" билды сразу, локально, то больше шансов заметить, что разные файлы с одинаковыми именами перетирают друг друга.
Настраивать $(OutDir)
тоже удобно одним общим props
-файликом вроде такого:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Output folders">
<BuildDirSuffix>$(Configuration).$(Platform)\</BuildDirSuffix>
<IntDir>$(SolutionDir)~output\~intermediate\$(BuildDirSuffix)\</IntDir>
<OutDir>$(SolutionDir)~output\$(BuildDirSuffix)\</OutDir>
<OutputPath>$(OutDir)</OutputPath>
</PropertyGroup>
Здесь использутся $(SolutionDir)
потому что эта настройка нужна только для Студии, на билд-сервере путь задаётся билд-конфигурацией. Нужно только удалить $(OutDir)
и $(IntDir)
из каждого .vcxproj
-файла.
Если кому-то всё же неудобно, что все файлы валятся в одну папку, то достаточно изменить BuildDirSuffix
на $(Configuration).$(Platform)\$(MSBuildProjectName)
.
Если же кому-то неудобно искать бинарники вдалеке от сорцов, то в Студии легко создать тул, чтобы открывать целевую папку: "Tools > External Tools > Add", Title="Open target director&y", Command="C:\Windows\explorer.exe", Arguments="$(BinDir)", после чего комбинация "Alt+T-Y" в студии будет мгновенно открывать папку с построенными бинарниками.
Не надо так делать: <OutDir>$(SolutionDir)~output\$(BuildDirSuffix)\</OutDir>
. Это убивает возможность перегрузить OutDir "снаружи" при дальнейшей настройке сборки.
Такие вещи надо писать в OutputPath, а OutDir сформируется на основе OutputPath средствами SDK.
Насколько я помню, это справедливо для проектов C#, но у C++ похоже собственная гордость. Если я убираю <OutDir>
и оставляю только <OutputPath>$(SolutionDir)~output\$(BuildDirSuffix)\</OutputPath>
, то .vcxproj
начинает кидать всё в $(SolutionDir)$(Platform)\$(Configuration)
, т.е. например в \x64\Release\
на уровне солюшена.
Под "перегрузить снаружи" вы имеете в виду передать через командную строку msbuild.exe /p:OutDir="...\\"
? Вроде командная строка имеет приоритет над пропсами? Я не сталкивался с проблемами тут, можно пояснить детали?
Замечу что при формировании списков файлов CopyFilesToFolder можно использовать wildcards.
Оказалось, что wildcards не поддерживаются VisualStudio.
Так что, для копирования директории приходится по-прежнему использовать xcopy в PostBuildEvent.
Причём тут вообще студия? Собирает-то проект не она.
Собирает-то проект не она
Студия загружает проект. А собирает его потом с помощью MsBuild.
Но если проект содержит ошибки или неподдерживаемые студией фичи, то студия не может загрузить такой проект.
Конечно, можно не загружать проект в студию, а собирать его из командной строки. Но зачем тогда Студия?
Так вот, студия всегда поддерживала пользовательские таргеты и никогда не заглядывала внутрь них. Сами таргеты читаются уже msbuild, а не студией.
С элементами проекта сложнее — студия пытается читать их и управлять ими. Но даже там студия спокойно переваривает любые пользовательские элементы проекта, даже те о которых вообще не знает.
В частности, wildcards старые версии проектной системы студии, насколько я знаю, раскрывают при чтении проекта и корёжат при сохранении. Но это единственный баг который я видел.
Если я правильно помню то они не показываются правильно в проекте, но сборка msbuild при этом работает. Однако xcopy имхо тут худшее решение - и не показывается и работает не так хорошо как msbuild.
Если я правильно помню то они не показываются правильно в проекте, но сборка msbuild при этом работает.
Студия просто не загружает проект если в нем есть CopyFilesToFolder с wildcard. Сообщает об ошибке.
Однако xcopy имхо тут худшее решение - и не показывается и работает не так хорошо как msbuild.
Команда xcopy нормально показывается в настройках, в разделе "PostBuildEvent".
А при сборке в окне "Output" выводятся имена копируемых файлов и сообщение "11 File(s) copied"
Однако xcopy имхо тут худшее решение
А какое лучшее решение?
Задача такая: В исходниках есть папки "debug" и "release", каждая содержит по десятку dll. При сборке из нужно скопировать dll-файлы из папки, соответствующей конфигурации, в OutDir.
Я сейчас специально перепроверил - 2022я студия загружает проект с wildcards. Пишет предупреждение при загрузке что при попытке изменить такой проект все может сломаться и упасть, но загружает. Начиная с 2019й Студии там даже добавили пару параметров специально для подобных проектов включающие возможность видеть данные из wildcards в solution или сделать проект read-only чтобы исключить проблемы с сохранением.
https://learn.microsoft.com/en-us/cpp/build/reference/vcxproj-files-and-wildcards
Проблема с xcopy в том что Студия не знает надо ли копировать файлы или нет и каждый раз при сборке выполняет xcopy. Это неудобно, особенно в больших солюшнах где проектов много и между ними куча dependency.
Да, сорри, был неправ. Wildcards поддерживаются.
CopyFilesToFolder работает. Но, к сожалению, работает молча: ничего не выводится в окно output при уровне Minimal. Можно это как-то исправить?
Так ведь уровень Minimal потому и называется так. Поставьте другой.
Да, но для ClCompile-items при Minimal-output выводится имя файла. И видно, что сборщик что-то делает.
А CopyFilesToFolder-items - полная тишина. Просто появляются файлы.
Можно ли как-то повысить информативность? Или добавить свои сообщения?
Вообще, CopyFilesToFolder как-то совсем плохо документированы.
Мой опыт с копированием файлов:
Item CopyFileToFolders:
Из коробки работают Rebuild, Build (Incremental), Clean. Но, при уровне Minimal-output, все это делается совершенно молча. Даже Rebuild.
Отсутствует документация.
Пользовательский Item и таргет с Copy-task:
Clean пришлось делать самостоятельно.
А Build не могу заставить работать. Если удалить часть файлов, то он этого не замечает, говорит, что все "up to date". Похоже, MsBuild проверяет файл *.lastbuildstate, и, если он актуальный, то игнорирует все таргеты и их Inputs и Outputs и завершает работу.
Вообще, странно. Вроде, простая и обычная задача: скопировать файл в выходную директорию, а нормального решения нет. А ведь нынешнему MsBuild-у уже 14 лет.
PS.
В статье есть опечатка в слове "CopyFileToFolders". Поправьте, пожалуйста.
Такая же опечатка есть в моих комментариях (я копипастил из статьи). Там, похоже, уже не исправить.
Если удалить часть файлов, то он этого не замечает, говорит, что все "up to date"
А .tlog файлы заполняете как описано в статье?
В статье есть опечатка в слове "CopyFileToFolders"
Спасибо за инфу про опечатку! Поправил
А .tlog файлы заполняете как описано в статье?
Да, действительно, я это упустил. И в результате, U2DCheck не запускал MSBuild.
Спасибо!
Поправил, заработало.
Вопросы:
Какой смысл несет имя tlog файла? В вашем примере это protobuf.read.1.tlog
Первая часть - что угодно?
Вторая - read/write.
Третья - какой-то номер?
Если удалять целевые файлы и вызывать Build, то они восстанавливаются. Но tlog-файлы при этом неограниченно растут - в них дописывается инфа о восстановленных файлах. Это нормально?
При Clean или Rebuild они очищаются.
По поводу получения детальных отчетов от U2DCheck. Не все так просто.
В указанной статье рекомендуют поставить флаг:[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0\General] "U2DCheckVerbosity"=dword:00000001
Но это, наверное, для 12 (2013) версии Студии. А для 2022 там, наверное, должно быть 17.0
У меня в реестре оказалось аж четыре таких раздела:
![](https://habrastorage.org/getpro/habr/upload_files/6c1/ac7/a95/6c1ac7a95a05bcdc80fc2cbb3707d698.png)
Я добавил U2DCheckVerbosity в каждый из четырех 17.0 разделов. Не заработало.
Еще в UI (Tools/Options) похожая настройка для SDK-style проектов. Тоже не помогло
Вроде, U2DCheckVerbosity работает, но только для C# проектов.
Настраиваем удобную сборку проектов в Visual Studio