В моих недавних постах в блоге ( часть 1 , часть 2 ) я подробно описал, как выполнить микропроцессор для Java и C / C ++ с JMH и Hayai . Я представил общий подход к исполнению на основе Gradle .
Сегодня я хочу улучшить общую структуру проекта . В прошлый раз я уже упоминал, что структура проектов Gradle не оптимальна. В первой части я в общих чертах повторю основную цель и результаты предыдущих статей, во-вторых представлю некоторые новые требования и, наконец, представлю вам более гибкую структуру модулей для разделения производственного кода и тестов, которая затем будет встроена в кросс языковой супер-проект.
1.) Что мы рассмотрели до сих пор
Чтобы отслеживать наш список Todo, вот выдержка из части 1 :
Предположим, у нас есть проект на Java и C ++, в котором реализованы одни и те же алгоритмы. Мы хотим сравнить и сравнить производительность горячих частей кода. Мы также хотим отслеживать изменения во времени, когда исходный код проекта растет и изменяется. Мы будем:
Сравнительный анализ Java-кода с JMH как часть сборки Gradle
Сравнительный код C ++ с Hayai
Интеграция бинарной компиляции c ++ с Gradle
Интеграция исполнения Hayai с Gradle
- Объедините проекты Java и C ++ в одну межязыковую цепочку сборки Gradle
- Совокупные результаты JMH и Hayai дают третий сложный результат
- Разделить код тестирования из проектов в отдельный проект и выделенный SCM.
- Автоматически отправлять агрегированные результаты тестирования в хитро структурированный репозиторий git, чтобы отслеживать текущие версии исходного кода и результаты тестирования.
Мы уже достигли всех вычеркнутых предметов. Сегодня мы сосредоточимся на синих предметах.
2.) Грязные вещи
Для полноты картины приведем скрипт сборки Gradle, в котором мы хотим определить новые требования сегодня:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'me.champeau.gradle:jmh-gradle-plugin:0.2.0'
}
}
repositories {
mavenCentral()
jcenter()
}
apply plugin: 'me.champeau.gradle.jmh'
apply plugin: 'java'
group = 'net.chroma'
version = '0.0.1-SNAPSHOT'
jmh {
jmhVersion = '1.6.1'
}
sourceCompatibility = 1.8
dependencies {
testCompile 'junit:junit:4.11'
}
Исходные данные:
- Этот сценарий сборки отвечает за сборку тестируемого артефакта и за выполнение микро-бенчмаркинга.
- Основной проект имеет зависимости от плагинов и библиотек, которые не нужны фактическому выходному артефакту.
- определения эталонных тестов должны находиться в одном корне исходного проекта вместе с основным кодом программы.
- Скрипт сборки содержит много деталей настройки, которые вы обычно не хотите видеть
Чтобы еще раз взглянуть на настройку проекта, перейдите по ссылке на ветку, с которой мы сегодня начинаем: Chroma @ github, Branch: crolabefra_starting_point .
Требования:
- Код бенчмаркинга должен находиться в отдельном проекте, который зависит от артефакта для бенчмарка, но сам артефакт должен быть независим от каких-либо библиотек бенчмаркинга или установки
- Инструмент автоматизации должен быть вызван один раз для выполнения всех тестов по всем прикрепленным проектам.
- Как разработчик мультиязычных тестов для разных языков, я хочу применить плагин фреймворка , который обрабатывает все настройки, необходимые для получения данных результатов (в правильном формате).
Оба новых требования приводят к полной реструктуризации текущей установки. В следующих разделах я покажу вам, как
- использовать git и подмодули для разделения кода продукта и тестов
- В целом картина выглядит взаимосвязанной с интеграцией Hayai в проекте по сравнительному анализу языка для кода Java и C / C ++.
- связать все вместе в аккуратный плагин Gradle (будущий пост)
3.) Отдельный проект ориентиров
Это статус-кво из статьи 1 :
chromarenderer-java
├── src
├── main
├── java
├── jmh
├── java
├── test
├── java
├── build.gradle
Мы стремимся к следующей структуре:
├── chromarenderer-java-benchmarks
├── src
├── jmh
├── java
├── build.gradle
├── settings.gradle [new]
├── chromarenderer-java
├── src
├── main
├── java
├── test
├── java
├── build.gradle
Преимущества очевидны:
- У нас есть два разных модуля Gradle, которые разделяют зависимости сборки и библиотеки: Разделение зависимостей, этапы сборки и пользовательский код Gradle.
- Код JMH не является частью источника продукта: разделение ответственности, видимость. Более чистая настройка проекта. Код для бенчмарка может быть зависимостью для извлечения из репозитория или библиотеки, или от того, что вам приходит в голову!
- В разных репозиториях git можно управлять двумя разными каталогами, значит, тестовый код и версии производственного кода больше не связаны друг с другом! Набор тестов может быть легко выполнен на разных версиях производственного кода SCM, просто проверив еще один коммит!
Единственный недостаток, который приходит с первым пунктом, заключается в том, что мы оказываемся в многомодульном проекте. Но настройка очень проста в Gradle. В основном требуется только файл settings.gradle в каталоге ‘chromarenderer-java-benchmarks’:
include 'chromarenderer-java'
include 'chromarenderer-java-benchmarks'
После изменения структуры каталогов, мы можем продолжить удаление частей из обоих файлов build.gradle, которые нам больше не нужны. Для проекта бенчмаркинга сначала:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'me.champeau.gradle:jmh-gradle-plugin:0.2.0'
}
}
repositories {
mavenCentral()
}
apply plugin: 'me.champeau.gradle.jmh'
apply plugin: 'java'
group = 'net.chroma'
version = '0.0.1-SNAPSHOT'
jmh {
jmhVersion = '1.6.1'
}
sourceCompatibility = 1.8
dependencies {
- testCompile 'junit:junit:4.11'
+ compile project(":chromarenderer-java)
}
Хорошо, нам больше не нужна зависимость JUnit, но теперь нам нужна зависимость от проекта для сравнения. Ну, мы не так много выиграли. Но подождите, что происходит с проектом производственного кода? Смотреть:
- buildscript {
- repositories {
- jcenter()
- }
- dependencies {
- classpath 'me.champeau.gradle:jmh-gradle-plugin:0.2.0'
- }
-}
repositories {
mavenCentral()
- jcenter()
}
-apply plugin: 'me.champeau.gradle.jmh'
apply plugin: 'java'
group = 'net.chroma'
version = '0.0.1-SNAPSHOT'
-jmh {
- jmhVersion = '1.6.1'
-}
sourceCompatibility = 1.8
dependencies {
testCompile 'junit:junit:4.11'
}
Вот что мы хотели увидеть! Ни единого доказательства того, что JMH проводит тесты по проекту! Как в ванильном проекте Gradle. Для эффективных примеров взгляните на примеры репозиториев проектов, которые я подготовил для этой статьи:
- chromarenderer-java-benchmarks @ репозиторий тестов github
- chromarenderer-java @ github хранилище производственного кода
4.) Подмодули Git
Теперь для всех вас, кто никогда раньше не использовал подмодули Git, вот краткое резюме: Грубо говоря, подмодули Git становятся удобными, когда вы хотите иметь
- репозитории в репозиториях
- один репозиторий для участия в других SCM
- импортировать сторонний код из других репозиториев, которыми вы не хотите управлять в своем собственном SCM.
4.1) Понятие подмодулей
Пример использования: предположим, у вас есть два проекта, в обоих из которых используется общий набор файлов:
PROJECT_A (own git repository)
├── src
├── main
├── java
├── common_assets
├── ....
PROJECT_B (own git repository)
├── src
├── main
├── groovy
├── common_assets
├── ...
Теперь вместо добавления одного и того же набора файлов в оба репозитория git (что, очевидно, приведет к путанице в версии, как только оба проекта захотят внести изменения в общие файлы, которые затем нужно будет синхронизировать вручную), будет более разумно перемещать все файлы в ‘common_assets’ в свой собственный репозиторий git:
PROJECT_A (git repository A)
├── src
├── main
├── java
├── common_assets (git repository C)
├── ....
PROJECT_B (git repository B)
├── src
├── main
├── groovy
├── common_assets (git repository C)
├── ...
Теперь вы можете указать вашему репозиторию проекта клонировать другой репозиторий в каталог и пометить этот каталог как субмодуль.
$> git submodule add [email protected]:bensteinert/repository-C.git
Затем Git клонирует этот субмодульный репозиторий, который останется полностью независимым от окружающего репозитория.
То, что теперь может вызывать головные боли, заключается в том, что родительский репозиторий отслеживает только текущую версию HEAD. Этапы и наборы изменений в файлах внутри подмодуля не видны родителю, распознаются только изменения локальной версии HEAD. Первоначально, после команды выше, набор изменений должен выглядеть следующим образом:
$> git status
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: common_assets
.gitmodules содержит информацию о недавно созданном подмодуле. Новый каталог common_assets теперь отображается в виде простого файла. Фоном было то, что git создал ссылку на каталог внутри папки .git (точнее, .git / modules / common_assets). После создания коммита из набора изменений git сохранил текущую извлеченную ревизию в подмодуле. Просто чтобы внести в него, возможно, больше ясности, войдите в подмодуль и измените ГОЛОВУ, проверив другой коммит. Эта операция создаст набор изменений, который будет выглядеть следующим образом
$> git status
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: common_assets (new commits)
Если вы вернетесь к предыдущему коммиту, изменения исчезнут. Эти изменения подмодуля HEAD могут быть добавлены в коммит, как и любое другое изменение файла. Единственное, что будет храниться внутри родительского репозитория, — это идентификатор фиксации текущего извлеченного HEAD. За тем, что происходит за кулисами в родительском каталоге, можно легко наблюдать с помощью команды ‘diff’ после повторного изменения версии подмодуля HEAD:
$> git diff
diff --git a/common_assets b/common_assets
index 244411c..d416be2 160000
--- a/common_assets
+++ b/common_assets
@@ -1 +1 @@
-Subproject commit 244411c0aad0b2278eb05622966ba59e1f48ab4b
+Subproject commit d416be2c15250a85bf7b993c0c4a41ff99162b56
Это снова выглядит как обычное изменение файла, но на самом деле вы изменили отслеживаемую версию HEAD подмодуля :).
Надеюсь, я внес некоторую ясность в концепцию. В любом случае я рекомендую руководство по git для дальнейшего чтения .
Общие преимущества
- Подмодуль позволяет объединять разные репозитории в одном родителе, не добавляя исходные файлы во второй SCM.
- Изменения в субмодульном контенте могут быть сделаны в любое время, потому что это обычный git-репозиторий сам по себе.
- Вы можете решить, когда вытащить новый контент в ваши подмодули.
- Проект, который состоит из различных подмодулей, может отслеживать различные комбинации, которые были зафиксированы в истории.
- Версии подмодуля HEAD могут быть легко изменены в любой момент в произвольной точке истории, не вызывая существенных изменений в наборе файлов.
4.2) Git подмодули и тесты?
Давайте вернемся к нашей новой структуре проекта. Объединение новой структуры с наличием тестов, «окружающих» производственный код, позволяет нам размещать их в независимых репозиториях. Затем проект для эталонного тестирования извлекается как субмодуль в эталонный проект:
├── chromarenderer-java-benchmarks ( git repository chromarenderer-java-benchmarks)
├── src
├── jmh
├── java
├── build.gradle
├── settings.gradle
├── chromarenderer-java (git submodule chromarenderer-java)
├── src
├── main
├── java
├── test
├── java
├── build.gradle
Важное замечание относительно клонирования репозитория git, которое содержит подмодули:
После клонирования репозитория git все содержащиеся в нем подмодули НЕ инициализируются и автоматически клонируются! У вас есть два варианта. Либо клонируйте репозиторий с рекурсивным параметром:
$> git clone --recursive [email protected]:bensteinert/chromarenderer-java-benchmarks.git
или, после обычного клона, выполните:
$> git submodule update --init
Все подмодули, имеющиеся в хранилище, будут выбраны, а текущая действующая версия HEAD будет извлечена.
5.) Отсутствующий «кросс» в межъязыковом бенчмаркинге
Мы говорили о части Java до сих пор. Понятия могут быть непосредственно применены к проекту C ++, который мы начали также во второй части, конечно. Чтобы не беспокоить вас одними и теми же вещами дважды, я просто сошлюсь на репозиторий git, подготовленный для проекта тестов Hayai : chromarenderer-cpp-benchmarks @ github . Вы узнаете точно такие же изменения:
- Все связанные с тестами вещи переместились на один каталог вверх
- Базовый проект chromarenderer-cpp избавился от всех зависимостей от инфраструктуры бенчмаркинга.
- Окружающий модуль бенчмаркинга определяет зависимость от основного модуля
Круто, похоже, что это можно обобщить :).
Значит, теперь у нас есть два разных проекта бенчмаркинга, которые можно запускать изолированно
5.1) Еще один супер проект
Теперь мы можем проверить оба репозитория и выполнить тесты по одной команде. Но нет, мы еще более ленивы. Нам нужен один кросс- языковой репозиторий с одной кросс- языковой сборкой. Итак, давайте добавим еще один уровень супер-проекта:
CroLaBeFra (git repository crolabefra) [new]
├── settings.gradle [new]
├── chromarenderer-java-benchmarks (git submodule chromarenderer-java-benchmarks)
├── [src ...]
├── build.gradle
├── settings.gradle
├── chromarenderer-java (git submodule chromarenderer-java)
├── [src ...]
├── build.gradle
├── chromarenderer-cpp-benchmarks ( git submodule chromarenderer-cpp-benchmarks)
├── [src ...]
├── build.gradle
├── settings.gradle
├── chromarenderer-cpp (git submodule chromarenderer-cpp)
├── [src ...]
├── build.gradle
5.2) Небольшая ловушка Gradle
Теоретически предлагаемая модель каталогов выглядит многообещающе. Но как только вы все перенесете и попробуете, вы удивитесь, что эта установка не работает. В вашем файле settings.gradle в супер-проекте CroLaBeFra вы, вероятно, попробуете что-то вроде:
1 2 3 |
rootProject.name = 'CroLaBeFra'
include 'chromarenderer-cpp-benchmarks'
include 'chromarenderer-java-benchmarks'
|
Теперь вас поразило то, что Gradle не может обнаружить несколько файлов «settings.gradle» в одном проекте. Следовательно, включения в подпроекты игнорируются. Но это не было бы Gradle, если бы не было обходного пути;). Поскольку супер проект должен знать, что он включает в себя больше подпроектов в сборку, вы можете включить файлы ‘settings.gradle’ в свой супер проект:
rootProject.name = 'CroLaBeFra'
include 'chromarenderer-cpp-benchmarks'
include 'chromarenderer-java-benchmarks'
apply from: 'chromarenderer-cpp-benchmarks/settings.gradle'
apply from: 'chromarenderer-java-benchmarks/settings.gradle
Теперь Gradle будет читать файлы подпроекта ‘settings.gradle’, а также, если он будет частью файла суперпроекта ‘settings.gradle’. Недостатком является то, что предполагаемая структура каталогов становится противоречивой. Задний план:
apply from: 'chromarenderer-java-benchmarks/settings.gradle'
в основном означает так же, как
include 'chromarenderer-java'
потому что контент будет просто оцениваться в контексте супер проекта. Но этот каталог и подпроект ‘chromarenderer-java’ не существует на уровне каталогов суперпроекта! Но опять же, это не было бы Gradle, если бы не было решения.
К счастью, Gradle сначала собирает все включения из проектов, прежде чем получить доступ к каталогам. Это означает, что мы можем изменить его позже, добавив следующие строки:
project(':chromarenderer').projectDir = new File(rootDir, 'chromarenderer-cpp-benchmarks/chromarenderer')
project(':chromarenderer-java').projectDir = new File(rootDir, 'chromarenderer-java-benchmarks/chromarenderer-java')
Это выглядит немного грязно, но, в конце концов, пока Gradle не поддерживает несколько файлов settings.gradle изначально, другого пути нет. Поскольку мы делаем все грязные вещи в супер-проекте, нам не нужно трогать наши подпроекты, и они остаются свободными от таких обходных путей ».
Окончательный результат дня со всеми внесенными изменениями и предложениями: CroLaBeFra-POC @ github
Клонировать (рекурсивно) и просто запустить
$> gradle runBenchmarks jmh
Миссия выполнена ?
6.) Заключение и текущая работа
Сегодня мы решили тему 7 и 5 из списка. Наш основной проект продукта не зависит от какой-либо сравнительной инфраструктуры и / или исходного кода. Простая структура каталогов в сочетании с мощью подмодулей Git позволяет легко управлять многомодульной установкой Gradle. С некоторой магией Gradle мы добавили еще один уровень проекта сверху, который позволяет получить доступ и выполнить все задачи бенчмаркинга с помощью одной команды. Глядя на список, мы почти закончили.
Сравнительный анализ Java-кода с JMH как часть сборки Gradle
Сравнительный код C ++ с Hayai
Интеграция бинарной компиляции c ++ с Gradle
Интеграция исполнения Hayai с Gradle
Объедините проекты Java и C ++ в одну межязыковую цепочку сборки Gradle
- Совокупные результаты JMH и Hayai дают третий сложный результат
Разделить код тестирования из проектов в отдельный проект и выделенный SCM.
- Автоматически отправлять агрегированные результаты тестирования в хитро структурированный репозиторий git, чтобы отслеживать текущие версии исходного кода и результаты тестирования.
Но у меня есть еще одна тема, которую я хотел бы добавить в список:
- Извлеките все настройки для бенчмаркинга из файлов build.gradle в простые в использовании плагины Gradle, чтобы предложить их всем вам :). Общая цель состоит в том, чтобы иметь набор различных плагинов для разных языков, которые могут быть применены к проекту бенчмаркинга. В идеале единственное, что вам нужно сделать, это
apply plugin: 'com.comsysto.gradle.crolabefra.cpp'
вместо того, чтобы скопировать тридцать строк кода скрипта Gradle. Звучит хорошо? Следите за моей следующей статьей!
Пока!