Русский
Русский
English
Статистика
Реклама

Сборка проекта

Nix воспроизводимая сборка

11.05.2021 20:09:12 | Автор: admin


Привет, Хаброюзеры!


Сегодня мы продолжим наш цикл статей о Nix и как мы в Typeable его используем.


Первый пост из серии, рассказывающий об основах языка Nix, можно прочитать здесь.


Так как мы очень любим и много используем Haskell для разработки, пример приложения будет на этом языке, но знание Haskell здесь никак не требуется. С лёгким допиливанием, код из примеров можно использовать и для сборки проектов на других языках.


Весь код для этой статьи можно найти в репозитарии на Github.


Проблема


Сборка и CI одни из самых больших проблем в разработке софта. На поддержку сборочной инфраструктуры очень часто уходит невообразимое количество ресурсов. С помощью Nix мы попытаемся если не исправить ситуацию, то сделать её немного более приемлемой. Nix позволяет нам обеспечить воспроизводимость сборки наших проектов, переносимость между разными ОС, унифицировать сборку компонентов на различных языках и так далее.


Наше приложение


Итак, начнём с приложения, которое мы хотим собрать. В нашем случае, это будет простая программа на языке Haskell, выводящая сообщение Hello world.


Наш Main.hs:


module Main wheremain :: IO ()main = putStrLn "Hello, World!"

Для сборки проекта без Nix мы используем утилиту stack (подробнее с ней можно ознакомиться здесь). В качестве описания проекта для stack требуется файл stack.yaml, содержащий список наших пакетов и resolver. Последнее это стабильный срез Hackage, базы пакетов для языка Haskell, в котором гарантируется, что все пакеты собираются и дружат друг с другом (NB подобных срезов крайне не хватает в других языках ): ).


stack.yaml:


resolver: lts-17.11packages:- hello-world

Рецепт сборки конкретного пакета находится в hello-world.cabal:


cabal-version:      2.4name:               hello-worldversion:            1.0synopsis:           Hello Worldlicense:            MITlicense-file:       LICENSEauthor:             Nickexecutable hello-world    main-is:          Main.hs    build-depends:    base >= 4 && < 5    hs-source-dirs:   src    default-language: Haskell2010    ghc-options:      -Wall -O2

Из этих двух файлов наш код на Nix и будет черпать информацию о том, что именно и как собирать. В качестве эксперимента, можем проверить, что наш код действительно запускается и работает:


$ stack run hello-worldHello, World!

Come to the dar^Wnix side, we have cookies!


Сам по себе stack отличное средство для сборки проектов на Haskell, но в нём не хватает многих возможностей. Для сборки программ на Haskell для Nix есть библиотека haskell.nix, разработанная компанией IOHK. Её-то мы и будем здесь использовать. Для начала, сделаем так, чтобы наш проект собирался с помощью Nix.


Haskell.nix позволяет нам в несколько строчек преобразовать всю информацию о сборке нашего проекта из .cabal-файлов и stack.yaml в derivation для Nix.


nix/stackyaml.nix:


{  # Импортируем последнюю версию haskell.nix с GitHub и инициализируем Nixpkgs с её использованием.  haskellNix ? import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/b0d03596f974131ab64d718b98e16f0be052d852.tar.gz") {}  # Здесь мы используем последнюю стабильную версию Nixpkgs. Версия 21.05 скоро выйдет :), nixpkgsSrc ? haskellNix.sources.nixpkgs-2009, nixpkgsArgs ? haskellNix.nixpkgsArgs, pkgs ? import nixpkgsSrc nixpkgsArgs}:let  # Создаём проект на базе stack. Для проектов Cabal есть функция cabalProject.  project = pkgs.haskell-nix.stackProject {    name = "hello-world";    # Derivation с исходным кодом проекта.    # Функция cleanGit копирует для сборки проекта только файлы, присутствующие в нашем git-репозитарии.    src = pkgs.haskell-nix.haskellLib.cleanGit {      name = "hello-world";      # Параметр src должен указывать на корневую директорию, содержащую stack.yaml.      src = ../.;      # keepGitDir оставляет директорию .git при сборке.      # Это может быть полезно, например, чтобы вставить хэш коммита в код.      keepGitDir = true;    };    # В параметре modules можно указать параметры сборки как для всех модулей сразу, так и для каждого в отдельности.    modules = [{      # doCheck отвечает за запуск юнит-тестов при сборке проекта, в том числе содержащихся во всех зависимостях.      # Здесь мы этого хотим избежать, поэтому этот параметр лучше всего ставить false и включить только для нужных      # пакетов.      doCheck = false;      # Добавим для нашего Hello World флаг -Werror.      packages.hello-world.components.exes.hello-world.ghcOptions = [ "-Werror" ];    }];  };# Наружу из этого файла мы выставляем project -- наш проект, а также pkgs -- срез nixpkgs, который мы будем использовать дальше.in { inherit project; inherit pkgs; }

Давайте проверим, что наш проект теперь можно собрать через Nix. Для этого достаточно команды nix build. Как и всегда, в текущей директории будет создана символическая ссылка result, содержащая результаты сборки.


$ nix build project.hello-world.components.exes$ ./result/bin/hello-worldHello, World!

Отлично! Небольшой магией выше мы обеспечили полностью воспроизводимую сборку нашего проекта, вплоть до всех системных зависимостей. Поехали дальше!


Dockerfile? Какой Dockerfile?


Сейчас 2021 год, и очень многие компании используют Docker для деплоя и запуска сервисов. Typeable здесь не будет исключением. В составе nixpkgs есть весьма удобный инструментарий для сборки контейнеров под названием dockerTools. Более подробно с его возможностями можно ознакомиться по ссылке, я лишь покажу, как мы с его помощью упаковываем наш код в контейнеры. Полностью код можно посмотреть в файле nix/docker.nix.


Для начала нам понадобится исходный контейнер, в который мы закинем всё что нам нужно. Nix позволяет собрать контейнер полностью с нуля без каких-либо лишних компонентов, но, тем не менее, этот подход не всегда удобен. Иногда, особенно во внештатных ситуациях, приходится залезать в контейнер руками через командную строку. Поэтому здесь мы используем CentOS.


sourceImage = dockerTools.pullImage {  imageName = "centos";  imageDigest = "sha256:e4ca2ed0202e76be184e75fb26d14bf974193579039d5573fb2348664deef76e";  sha256 = "1j6nplfs6999qmbhjkaxwjgdij7yf31y991sna7x4cxzf77k74v3";  finalImageTag = "7";  finalImageName = "centos";};

Здесь всё очевидно для всех, кто когда-либо работал с Docker. Мы говорим Nix, какой образ из публичного Docker Registry мы хотим использовать и что дальше мы будем на него ссылаться как на sourceImage.


Для сборки самого образа в dockerTools есть функция buildImage. У неё довольно много параметров, и часто проще написать свою обёртку над ней, что мы и сделаем:


makeDockerImage = name: revision: packages: entryPoint:  dockerTools.buildImage {    name = name;    tag = revision;    fromImage = sourceImage;    contents = (with pkgs; [ bashInteractive coreutils htop strace vim ]) ++ packages;    config.Cmd = entryPoint;  };

Наша функция makeDockerImage принимает четыре параметра: имя контейнера, его версия (в Typeable мы обычно используем хэш коммита из git в качестве тега), пакеты, которые мы хотим включить, и точку входа при запуске контейнера. Внутри же мы ссылаемся на образ с CentOS как основу (fromImage), плюс добавляем всякие утилиты, крайне полезные при экстренных случаях.


И, наконец, создадим образ с нашим великолепным приложением.


hello-world = project.hello-world.components.exes.hello-world;helloImage = makeDockerImage "hello"   (if imageTag == null then "undefined" else imageTag)  [ hello-world ]  [ "${hello-world}/bin/hello-world"  ];

Для начала мы создадим алиас для нужного нам пакета, чтобы не писать project.hello-world... повсюду. Дальше, вызвав написанную ранее функцию makeDockerImage, мы создаём образ контейнера с пакетом hello-world. В качестве тэга будет указан параметр imageTag, передаваемый снаружи, либо "undefined" если ничего не передано.


Проверим сборку:


$ nix build --argstr imageTag 1.0 helloImage[4 built, 0.0 MiB DL] $ ls -l resultlrwxrwxrwx 1 user users 69 May 11 13:12 result -> /nix/store/56qqhiwahyi46g6mf355fjr1g6mcab0b-docker-image-hello.tar.gz

Через пару минут или даже быстрее мы получим символическую ссылку result, указывающую на наш готовый образ. Проверим, что всё получилось.


$ docker load < result 76241b8b0c76: Loading layer [==================================================>]  285.9MB/285.9MBLoaded image: hello:1.0$ docker run hello:1.0Hello, World!

Заключение


В итоге, с помощью сравнительно небольшого количества кода, у нас получилось сделать воспроизводимую сборку нашего проекта на Haskell. Точно так же, заменив haskell.nix на что-то другое, можно поступить с проектами на других языках: в nixpkgs есть встроенные средства для C/C++, Python, Node и других популярных языков.


В следующей статье цикла я расскажу о частых проблемах, которые возникают при работе с Nix. Stay tuned!

Подробнее..

Опыт команды PVS-Studio повышение производительности C анализатора на Windows при переходе на Clang

31.05.2021 18:12:13 | Автор: admin

С самого своего начала C++ анализатор PVS-Studio для Windows (тогда еще Viva64 версии 1.00 в 2006 году) собирался компилятором MSVC. С выходом новых релизов C++ ядро анализатора научилось работать на Linux и macOS, и структура проекта была переведена на использование CMake. Но под Windows сборка по-прежнему происходила с помощью компилятора MSVC. 29 апреля 2019 года разработчики Visual Studio объявили о включении в свою среду разработки набора утилит LLVM и компилятора Clang. И сейчас у нас наконец дошли руки, чтобы попробовать его в действии.

Тестирование производительности

В качестве бенчмарка воспользуемся нашей утилитой для регрессионного тестирования анализатора под названием SelfTester. Суть её работы заключается в анализе набора разных проектов и сравнении результатов анализа с эталонными. Например, если при каких-то правках в ядре анализатора появились ложные предупреждения или пропали правильные, значит появилась регрессия, которую надо исправить. Более подробно про SelfTester можно прочитать в статье "Лучшее враг хорошего".

Среди тестовой базы достаточно разнообразные по объёму кода проекты. Как правило, если рабочий компьютер или тестовый сервер не нагружен, то время тестирования SelfTester'ом на одной и той же версии ядра варьируется в пределах погрешности. В случае если производительность анализатора не уберегли, это значительно скажется на общем времени тестирования.

После того как сборка C++ ядра перешла на Clang, SelfTester стал проходить на 11 минут быстрее.

Выигрыш по производительности в 13% это довольно заметно, учитывая, что достаточно просто поменять компилятор, не так ли?

Минусы тоже есть, но незначительные. Сборка дистрибутива замедлилась на 8 минут, а размер исполняемого файла подрос на 1,6 Мбайт (из них ~500 Кбайт из-за статической линковки рантайма).

Видимо, производительность достигается более долгим этапом LTO (большую часть сборки занимает именно линковка) и более агрессивным раскручиванием циклов и встраиванием функций.

Далее хочется поделиться подводными камнями, возникшими в процессе перехода.

Генерация сборки под Clang

Скрипты CMake позволяют нам собирать свой код всеми мейнстримными компиляторами под нужные операционные системы.

Прежде всего необходимо установить компоненты компилятора Clang через Visual Studio Installer.

Clang-cl это так называемый "драйвер", который позволяет использовать clang с параметрами от cl.exe. Таким образом, он должен прозрачно взаимодействовать с MSBuild, практически как родной компилятор.

Также можно воспользоваться официальными сборками от проекта LLVM, которые можно найти на их репозитории GitHub. Однако для них нужно установить дополнительный плагин, чтобы Visual Studio смогла найти компиляторы. Имя toolset'а будет llvm, а не clangcl, как показано дальше в примерах.

Указываем toolchain в команде генерации solution для Visual Studio:

cmake -G "Visual Studio 16 2019" -Tclangcl <src>

Либо используем GUI:

Открываем получившийся проект, собираем. И, конечно же, получаем пачку ошибок.

Чиним сборку

Хоть сlang-cl внешне и ведет себя как CL, под капотом это совсем другой компилятор, со своими приколами.

Мы стараемся не игнорировать предупреждения компиляторов, поэтому используем флаги /W4 и /WX. Однако Clang может генерировать дополнительные предупреждения, которые сейчас мешают сборке. Пока выключим их:

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")  ....  if (WIN32)    add_compile_options(-Wno-error=deprecated-declarations                        -Wno-error=reorder-ctor                        -Wno-error=format-security                        -Wno-error=macro-redefined                        -Wno-error=bitwise-op-parentheses                        -Wno-error=missing-field-initializers                        -Wno-error=overloaded-virtual                        -Wno-error=invalid-source-encoding                        -Wno-error=multichar                        -Wno-unused-local-typedef                        -Wno-c++11-narrowing)  ....  endif()endif()

Немного получше.

Компиляторы GCC и Clang имеют встроенную поддержку типа int128, в отличие от MSVC под Windows. Поэтому в своё время была написана обертка с реализацией Int128 для Windows (на ассемблерных вставках и обернутая ifdef'ами, в лучших традициях C/C++). Поправим определения для препроцессора, заменив:

if (MSVC)  set(DEFAULT_INT128_ASM ON)else ()  set(DEFAULT_INT128_ASM OFF)endif ()

на

if (MSVC AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")  set(DEFAULT_INT128_ASM ON)else ()  set(DEFAULT_INT128_ASM OFF)endif ()

Обычно библиотеку с builtin'ами линкеру (lld) передает драйвер компилятора, будь то clang.exe или clang-cl.exe. Но в данном случае линкером заправляет MSBuild напрямую, который не знает, что нужно её использовать. Соответственно, драйвер никак не может передать флаги линкеру, поэтому приходится разбираться самим.

if (CMAKE_GENERATOR MATCHES "Visual Studio")  link_libraries("$(LLVMInstallDir)\\lib\\clang\\\${CMAKE_CXX_COMPILER_VERSION}\\lib\\windows\\\clang_rt.builtins-x86_64.lib")else()  link_libraries(clang_rt.builtins-x86_64)endif()

Ура! Сборка заработала. Однако дальше при запуске тестов нас ждала куча ошибок сегментации:

Отладчик при этом показывает какое-то странное значение в IntegerInterval, а на самом деле проблема находится немного дальше:

В разных структурах для Dataflow-механизма активно применяется ранее упомянутый тип Int128, а для работы с ним используются SIMD-инструкции. И падение вызвано невыровненным адресом:

Инструкция MOVAPS перемещает из памяти набор чисел с плавающей запятой в регистры для SIMD-операций. Адрес при этом обязан быть выровнен, в его конце должен стоять 0, а оказалась 8. Придется помочь компилятору, задав правильное выравнивание:

class alignas(16) Int128

Порядок.

Последняя проблема вылезла из-за Docker-контейнеров:

Сборка под MSVC всегда делалась со статической линковкой рантайма, а для экспериментов с Clang рантайм переключили на динамический. Оказалось, что в образах с Windows по умолчанию не установлены Microsoft Visual C++ Redistributable. Решили вернуть статическую линковку, чтобы у пользователей не возникало таких же неприятностей.

Заключение

Несмотря на то, что пришлось немного повозиться с подготовкой проекта, мы остались довольны ростом производительности анализатора более чем на 10%.

Последующие релизы PVS-Studio на Windows будут собираться с помощью компилятора Clang.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Alexey Govorov, Sergey Larin. PVS-Studio Team: Switching to Clang Improved PVS-Studio C++ Analyzer's Performance.

Подробнее..

Материалы митапа для андроид-инженеров поиск проблем сборки, защита от них и работа с Gradle

16.03.2021 18:09:12 | Автор: admin

Недавно прошёл наш Android meetup, где ребята из платформенной команды Авито делились своим опытом работы с Gradle, показывали способы защиты от частых проблем при сборке проектов и рассказывали о нашем подходе к решению проблем.

Собрали в посте видеозаписи выступлений с таймкодами и ссылки на презентации спикеров.

Gradle в 2021: сonvention plugins workshop Дмитрий Воронин

Воркшоп о способе организации проектов,который позиционируется командой Gradle как идиоматический.

Тема будет интересна инженерам с достаточно большими проектами, которые содержат несколько модулей. Будет полезно посмотреть, если есть сложности с проектами и непонимание, как правильно структурировать Gradle-код и переиспользовать конфигурации между модулями. Ну или просто тем инженерам, кто хочет узнать, как Gradle идиоматически задумывает конфигурацию.

00:00 Представление спикера и темы

05:27 Проект, который будет примером в воркшопе

06:44 Лайвкодинг: пошаговая оптимизация проекта

28:31 Ответы на вопросы

Полезные ссылки:

Lint для сборки: как защищаться от проблем при сборке проекта Евгений Кривобоков

Женя рассказал, как мы защищаемся от довольно частых проблем при сборках проектов. В докладе нет частей про диагностику проблем и ускорение сборки, он сконцентрирован на том, что делать для предотвращения найденных проблем в будущем. Подход, о котором идёт речь, не универсальный, но довольно много где применим.

00:00 Представление спикера и темы

01:38 Какие бывают проблемы

09:05 Как контролировать окружение

14:16 Пример специфической проблемы для конкретного проекта и её решения

20:04 Зачем вообще писать свои проверки

22:35 Ответы на вопросы

Посмотреть презентацию Евгения

Gradle build scan на коленке Сергей Боиштян

На боевом примере Сергей разбирает, как мы упростили поиск ошибок в своих CI-сборках. Вы узнаете, как мы применяем продуктовый подход в решении проблем и немного о том, как работаем с Gradle.

Доклад будет полезен тимлидам в больших командах, разработчикам, которые настраивают CI/CD и разработчикам, которые решают любого рода проблемы.

00:00 Представление спикера, темы и её пользы

04:12 Поиск проблемы: разбираем на примере падения сборки

06:51 Определяем приоритет задач по RICE

14:36 Как мы искали решение проблемы

18:30 Пишем прототип с помощью TestProjectGenerator

26:11 Версия инструмента 1.0

30:30 Отдаём инструмент пользователям и смотрим на результат

34:02 Сравнение: как было и как стало

36:52 Ответы на вопросы

Посмотреть презентацию Сергея

На этом всё, до встречи на новых митапах!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru