View on GitHub

blog

Введение в профили сборки

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

Однако иногда переносимость невозможна. При определенных условиях плагины могут нуждаться в настройке с указанием путей локальной файловой системы. В других случаях потребуется немного другой набор зависимостей, и, возможно, потребуется немного изменить имя артефакта проекта. А в других случаях вам может даже потребоваться включить целый плагин в жизненный цикл сборки в зависимости от обнаруженной среды сборки.

Для решения этих проблем Maven поддерживает профили сборки. Профили указываются с использованием подмножества элементов, доступных в самой POM (плюс один дополнительный раздел), и запускаются любым из множества способов. Они изменяют POM во время сборки и предназначены для использования в дополнительных наборах, чтобы дать эквивалентные, но разные параметры для набора целевых сред (например, путь к корневому каталогу сервера приложений при разработке, тестировании и производственные среды). Таким образом, профили могут легко привести к разным результатам сборки у разных членов вашей команды. Однако при правильном использовании профили можно использовать, сохраняя при этом переносимость проекта. Это также минимизирует использование опции -f в maven, которая позволяет пользователю создавать другой POM с другими параметрами или конфигурацией для сборки, что делает его более удобным в обслуживании, поскольку он работает только с одним POM.

Какие бывают типы профиля? Где каждый определяется?

Как можно активировать профиль? Как это зависит от типа используемого профиля?

Профиль можно активировать несколькими способами:

Подробности об активации профиля

Профили можно явно указать с помощью флага командной строки -P.

За этим флагом следует список используемых идентификаторов профилей, разделенных запятыми. Профили, указанные в параметре, активируются в дополнение к любым профилям, которые активируются конфигурацией активации или разделом <activeProfiles> в settings.xml.

mvn groupId:artifactId:goal -P profile-1,profile-2

Профили можно активировать в настройках Maven через раздел <activeProfiles>. Этот раздел принимает список элементов <activeProfile>, каждый из которых содержит идентификатор профиля внутри.

<settings>
  ...
  <activeProfiles>
    <activeProfile>profile-1</activeProfile>
  </activeProfiles>
  ...
</settings>

Профили, перечисленные в теге <activeProfiles>, будут активироваться по умолчанию каждый раз, когда проект будет использовать его.

Профили могут запускаться автоматически в зависимости от обнаруженного состояния среды сборки. Эти триггеры указываются в разделе <activation> в самом профиле. В настоящее время это обнаружение ограничено сопоставлением префиксов версии JDK, наличием системного свойства или значением системного свойства. Вот несколько примеров.

Следующая конфигурация активирует профиль, когда версия JDK начинается с «1.4» (например, «1.4.0_08», «1.4.2_07», «1.4»):

<profiles>
  <profile>
    <activation>
      <jdk>1.4</jdk>
    </activation>
    ...
  </profile>
</profiles>

Диапазоны также можно использовать, начиная с Maven 2.1 (дополнительную информацию см. В Синтаксисе диапазонов версий Enforcer). Следующие почести версии 1.3, 1.4 и 1.5.

<profiles>
  <profile>
    <activation>
      <jdk>[1.3,1.6)</jdk>
    </activation>
    ...
  </profile>
</profiles>

Примечание: верхняя граница, такая как 1.5], скорее всего, не будет включать большинство выпусков 1.5, поскольку они будут иметь дополнительный «патч» выпуск, такой как _05, который не принимается во внимание в указанном выше диапазоне.

Следующий будет активирован в зависимости от настроек ОС. Дополнительные сведения о значениях ОС см. В подключаемом модуле Maven Enforcer.

<profiles>
  <profile>
    <activation>
      <os>
        <name>Windows XP</name>
        <family>Windows</family>
        <arch>x86</arch>
        <version>5.1.2600</version>
      </os>
    </activation>
    ...
  </profile>
</profiles>

Профиль, указанный ниже, будет активирован, если для системного свойства «отладка» указано любое значение:

<profiles>
  <profile>
    <activation>
      <property>
        <name>debug</name>
      </property>
    </activation>
    ...
  </profile>
</profiles>

Следующий профиль будет активирован, если системное свойство «отладка» вообще не определено:

<profiles>
  <profile>
    <activation>
      <property>
        <name>!debug</name>
      </property>
    </activation>
    ...
  </profile>
</profiles>

Следующий профиль будет активирован, если системное свойство «отладка» не определено или определено со значением, которое не является «истиной».

<profiles>
  <profile>
    <activation>
      <property>
        <name>debug</name>
        <value>!true</value>
      </property>
    </activation>
    ...
  </profile>
</profiles>

Чтобы активировать это, вы должны ввести один из них в командной строке:

mvn groupId:artifactId:goal
mvn groupId:artifactId:goal -Ddebug=false

В следующем примере сработает профиль, если системное свойство «среда» указано со значением «test»:

<profiles>
  <profile>
    <activation>
      <property>
        <name>environment</name>
        <value>test</value>
      </property>
    </activation>
    ...
  </profile>
</profiles>

Чтобы активировать это, вы должны ввести это в командной строке:

mvn groupId:artifactId:goal -Denvironment=test

Начиная с Maven 3.0, профили в POM также можно активировать на основе свойств активных профилей из файла settings.xml.

Примечание. Переменные среды, такие как FOO, доступны как свойства формы env.FOO. Также обратите внимание, что имена переменных среды нормализованы до верхнего регистра в Windows.

В этом примере сработает профиль, если сгенерированный файл target/ generated-sources/axistools/wsdl2java/org/apache/maven отсутствует.

<profiles>
  <profile>
    <activation>
      <file>
        <missing>target/generated-sources/axistools/wsdl2java/org/apache/maven</missing>
      </file>
    </activation>
    ...
  </profile>
</profiles>

Начиная с Maven 2.0.9, теги <exists> и <missing> можно было интерполировать. Поддерживаемые переменные - это системные свойства, такие как ${user.home}, и переменные среды, такие как ${env.HOME}. Обратите внимание, что свойства и значения, определенные в самом POM, здесь недоступны для интерполяции, например активатор из приведенного выше примера не может использовать ${project.build.directory}, но ему необходимо жестко запрограммировать целевой путь.

Профили также могут быть активными по умолчанию, используя следующую конфигурацию:

<profiles>
  <profile>
    <id>profile-1</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    ...
  </profile>
</profiles>

Этот профиль будет автоматически активен для всех сборок, если другой профиль в том же POM не будет активирован одним из ранее описанных методов. Все профили, которые активны по умолчанию, автоматически деактивируются, когда профиль в POM активируется в командной строке или через его конфигурацию активации. Деактивация профиля

Начиная с Maven 2.0.10, один или несколько профилей можно деактивировать с помощью командной строки, добавив к их идентификатору префикс либо символа ‘!’ или ‘-‘, как показано ниже:

mvn groupId:artifactId:goal -P !profile-1,!profile-2

Это можно использовать для деактивации профилей, помеченных как activeByDefault, или профилей, которые в противном случае были бы активированы через их конфигурацию активации. Какие области POM можно настроить для каждого типа профиля? Почему?

Теперь, когда мы поговорили о том, где указать профили и как их активировать, будет полезно поговорить о том, что вы можете указать в профиле. Как и в случае с другими аспектами конфигурации профиля, этот ответ непростой.

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

Профили, указанные во внешних файлах (например, в settings.xml или profiles.xml), не переносятся в самом строгом смысле. Все, что имеет высокую вероятность изменения результата сборки, ограничивается встроенными профилями в POM. Такие вещи, как списки репозиториев, могут быть просто проприетарным репозиторием одобренных артефактов и не изменят результат сборки. Таким образом, вы сможете изменять только разделы <repositories> и <pluginRepositories>, а также дополнительный раздел <properties>.

Раздел <properties> позволяет указать пары ключ-значение произвольной формы, которые будут включены в процесс интерполяции для POM. Это позволяет вам указать конфигурацию плагина в виде ${profile.provided.path}. Профили в POM

С другой стороны, если ваши профили могут быть разумно указаны внутри POM, у вас есть гораздо больше возможностей. Компромисс, конечно же, заключается в том, что вы можете изменять только этот проект и его подмодули. Поскольку эти профили заданы как встроенные и, следовательно, имеют больше шансов на сохранение переносимости, разумно сказать, что вы можете добавить в них дополнительную информацию без риска того, что эта информация будет недоступна другим пользователям.

Профили, указанные в POM, могут изменять следующие элементы POM:

<repositories>
<pluginRepositories>
<dependencies>
<plugins>
<properties> (not actually available in the main POM, but used behind the scenes)
<modules>
<reporting>
<dependencyManagement>
<distributionManagement>
a subset of the <build> element, which consists of:
    <defaultGoal>
    <resources>
    <testResources>
    <finalName>

Элементы POM за пределами <profiles>

Мы не разрешаем модификацию некоторых элементов POM вне профилей POM, потому что эти модификации среды выполнения не будут распространяться при развертывании POM в системе репозитория, что делает сборку этого проекта этим человеком полностью уникальной среди других. Хотя в некоторой степени это можно сделать с помощью параметров, указанных для внешних профилей, опасность ограничена. Другая причина заключается в том, что эта информация POM иногда повторно используется из родительского POM.

Внешние файлы, такие как settings.xml и profiles.xml, также не поддерживают элементы вне POM-профилей. Возьмем этот сценарий для развития. Когда эффективный POM развертывается в удаленном репозитории, любой человек может получить его информацию из репозитория и использовать ее для непосредственного создания проекта Maven. Теперь представьте, что если мы можем установить профили в зависимостях, что очень важно для сборки, или в любых других элементах за пределами POM-профилей в settings.xml, то, скорее всего, мы не можем ожидать, что кто-то другой будет использовать этот POM из репозитория и будет в состоянии построить это. И мы также должны подумать о том, как поделиться файлом settings.xml с другими. Обратите внимание, что слишком много файлов для настройки очень сбивает с толку и очень сложен в обслуживании. Суть в том, что, поскольку это данные сборки, они должны быть в POM. Одна из целей Maven 2 - объединить всю информацию, необходимую для запуска сборки, в один файл или иерархию файлов, которая является POM. Заказ профиля

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

Пример:

<project>
  ...
  <repositories>
    <repository>
      <id>global-repo</id>
      ...
    </repository>
  </repositories>
  ...
  <profiles>
    <profile>
      <id>profile-1</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <repositories>
        <repository>
          <id>profile-1-repo</id>
          ...
        </repository>
      </repositories>
    </profile>
    <profile>
      <id>profile-2</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <repositories>
        <repository>
          <id>profile-2-repo</id>
          ...
        </repository>
      </repositories>
    </profile>
    ...
  </profiles>
  ...
</project>

Это приводит к списку репозиториев: profile-2-repo, profile-1-repo, global-repo. Подводные камни профиля

Мы уже упоминали тот факт, что добавление профилей в вашу сборку может нарушить переносимость вашего проекта. Мы даже зашли так далеко, что подчеркнули обстоятельства, при которых профили могут нарушить переносимость проекта. Тем не менее, стоит повторить эти моменты в рамках более последовательного обсуждения некоторых подводных камней, которых следует избегать при использовании профилей.

При использовании профилей следует помнить о двух основных проблемных областях. Во-первых, это внешние свойства, обычно используемые в конфигурациях плагинов. Это создает риск нарушения переносимости вашего проекта. Другая, более тонкая область - это неполная спецификация естественного набора профилей. Внешние свойства

Определение внешнего свойства касается любого значения свойства, определенного вне pom.xml, но не определенного в соответствующем профиле внутри него. Наиболее очевидное использование свойств в POM - это конфигурация плагина. Хотя, безусловно, можно нарушить переносимость проекта без свойств, эти существа могут иметь незаметные эффекты, приводящие к сбою сборки. Например, указание путей к серверам приложений в профиле, указанном в файле settings.xml, может привести к сбою вашего плагина тестирования интеграции, когда другой пользователь из группы попытается построить без аналогичного settings.xml. Рассмотрим следующий фрагмент pom.xml для проекта веб-приложения:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.myco.plugins</groupId>
        <artifactId>spiffy-integrationTest-plugin</artifactId>
        <version>1.0</version>
        <configuration>
          <appserverHome>${appserver.home}</appserverHome>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>
  ...
</project>

Теперь в вашем локальном ${user.home}/.m2/settings.xml у вас есть:

<settings>
  ...
  <profiles>
    <profile>
      <id>appserverConfig</id>
      <properties>
        <appserver.home>/path/to/appserver</appserver.home>
      </properties>
    </profile>
  </profiles>
 
  <activeProfiles>
    <activeProfile>appserverConfig</activeProfile>
  </activeProfiles>
  ...
</settings>

Когда вы создаете этап жизненного цикла интеграционного тестирования, ваши интеграционные тесты проходят, поскольку указанный вами путь позволяет тестирующему плагину установить и протестировать это веб-приложение.

Однако, когда ваш коллега пытается выполнить сборку для тестирования интеграции, его сборка терпит неудачу, жалуясь на то, что она не может разрешить параметр конфигурации плагина <appserverHome> или, что еще хуже, что значение этого параметра - буквально ${appserver.home} - равно недействительный (если он вас вообще предупреждает).

Поздравляем, ваш проект теперь не переносится. Включение этого профиля в ваш pom.xml может помочь решить эту проблему, с очевидным недостатком, заключающимся в том, что каждая иерархия проекта (с учетом эффектов наследования) теперь должна указывать эту информацию. Поскольку Maven обеспечивает хорошую поддержку наследования проектов, можно закрепить такую ​​конфигурацию в разделе <pluginManagement> командного уровня POM или аналогичного и просто наследовать пути.

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

В дополнение к вышеупомянутому нарушителю переносимости, легко не охватить все случаи своими профилями. Когда вы делаете это, вы обычно оставляете одну из ваших целевых сред высокой и сухой. Давайте еще раз возьмем пример сниппета pom.xml сверху:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.myco.plugins</groupId>
        <artifactId>spiffy-integrationTest-plugin</artifactId>
        <version>1.0</version>
        <configuration>
          <appserverHome>${appserver.home}</appserverHome>
        </configuration>
      </plugin>
      ...
    </plugins>
  </build>
  ...
</project>

Теперь рассмотрим следующий профиль, который будет указан встроенным в файл pom.xml:

<project>
  ...
  <profiles>
    <profile>
      <id>appserverConfig-dev</id>
      <activation>
        <property>
          <name>env</name>
          <value>dev</value>
        </property>
      </activation>
      <properties>
        <appserver.home>/path/to/dev/appserver</appserver.home>
      </properties>
    </profile>
 
    <profile>
      <id>appserverConfig-dev-2</id>
      <activation>
        <property>
          <name>env</name>
          <value>dev-2</value>
        </property>
      </activation>
      <properties>
        <appserver.home>/path/to/another/dev/appserver2</appserver.home>
      </properties>
    </profile>
  </profiles>
  ..
</project>

Этот профиль очень похож на профиль из последнего примера, с несколькими важными исключениями: он явно ориентирован на среду разработки, добавлен новый профиль с именем appserverConfig-dev-2 и в нем есть раздел активации, который будет запускать его включение, когда системные свойства содержат env = dev для профиля с именем appserverConfig-dev и env = dev-2 для профиля с именем appserverConfig-dev-2. Итак, выполняем:

mvn -Denv=dev-2 integration-test

приведет к успешной сборке с применением свойств, заданных профилем appserverConfig-dev-2. И когда мы выполняем mvn -Denv=dev integration-test

это приведет к успешной сборке с применением свойств, заданных профилем appserverConfig-dev. Однако выполнение:

mvn -Denv=production integration-test

не будет успешной сборки. Почему? Поскольку результирующее неинтерполированное буквальное значение ${appserver.home} не будет подходящим путем для развертывания и тестирования вашего веб-приложения. При написании наших профилей мы не рассматривали случай производственной среды. «Производственная» среда (env = production) вместе с «тестовой» и, возможно, даже «локальной» составляют естественный набор целевых сред, для которых мы, возможно, захотим построить фазу жизненного цикла интеграции-тестирования. Неполная спецификация этого естественного набора означает, что мы эффективно ограничили наши допустимые целевые среды средой разработки. Ваши товарищи по команде - и, возможно, ваш менеджер - не увидят в этом юмора. Когда вы создаете профили для обработки таких случаев, обязательно учитывайте весь набор целевых перестановок.

Вкратце, профили пользователей могут действовать аналогичным образом. Это означает, что профили для обработки различных сред, привязанные к пользователю, могут срабатывать, когда команда добавляет нового разработчика. Хотя я полагаю, что это могло бы послужить полезным обучением для новичков, было бы просто нехорошо бросать их таким образом волкам. Опять же, обязательно продумайте весь набор профилей. Как узнать, какие профили действуют во время сборки?

Определение активных профилей поможет пользователю узнать, какие именно профили были выполнены во время сборки. Мы можем использовать подключаемый модуль справки Maven, чтобы узнать, какие профили действуют во время сборки.

  mvn help:active-profiles

У нас есть несколько небольших примеров, которые помогут нам лучше понять цель этого плагина в отношении активных профилей.

Из последнего примера профилей в pom.xml вы заметите, что есть два профиля с именами appserverConfig-dev и appserverConfig-dev-2, которым присвоены разные значения свойств. Если мы продолжим и выполним:

  mvn help:active-profiles -Denv=dev

Результатом будет маркированный список идентификаторов профиля со свойством активации «env = dev» вместе с источником, в котором он был объявлен. См. Образец ниже.

Следующие профили активны:

appserverConfig-dev (source: pom)

Теперь, если у нас есть профиль, объявленный в settings.xml (см. Образец профиля в settings.xml) и настроенный как активный профиль, выполните:

  mvn help:active-profiles

Результат должен быть примерно таким

Следующие профили активны:

appserverConfig (source: settings.xml)

Несмотря на то, что у нас нет свойства активации, профиль был указан как активный. Почему? Как мы упоминали ранее, автоматически активируется профиль, который был установлен в качестве активного профиля в файле settings.xml.

Теперь, если у нас есть что-то вроде профиля в settings.xml, который был установлен как активный профиль, а также запускал профиль в POM. Как вы думаете, какой профиль повлияет на сборку?

  mvn help:active-profiles -P appserverConfig-dev

Это будет список активированных профилей:

Следующие профили активны:

Несмотря на то, что в нем перечислены два активных профиля, мы не уверены, какой из них был применен. Чтобы увидеть влияние на сборку, выполните:

  mvn help:effective-pom -P appserverConfig-dev

Это выведет на консоль эффективный POM для этой конфигурации сборки. Обратите внимание, что профили в файле settings.xml имеют более высокий приоритет, чем профили в POM. Таким образом, здесь применен профиль appserverConfig, а не appserverConfig-dev.

Если вы хотите перенаправить вывод плагина в файл с именем effective-pom.xml, используйте параметр командной строки -Doutput=effective-pom.xml. Соглашения об именах

К настоящему времени вы заметили, что профили - это естественный способ решения проблемы различных требований к конфигурации сборки для разных целевых сред. Выше мы обсудили концепцию «естественного набора» профилей для решения этой ситуации и важность учета всего набора профилей, который потребуется.

Однако вопрос о том, как организовать и управлять развитием этого множества, также нетривиален. Подобно тому, как хороший разработчик стремится писать самодокументирующийся код, важно, чтобы идентификатор вашего профиля указывал на их предполагаемое использование. Один из хороших способов сделать это - использовать триггер общего системного свойства как часть имени профиля. Это может привести к именам типа env-dev, env-test и env-prod для профилей, которые запускаются системным свойством env. Такая система оставляет очень интуитивно понятный намек на то, как активировать сборку, ориентированную на конкретную среду. Таким образом, чтобы активировать сборку для тестовой среды, вам необходимо активировать env-test, выполнив:

mvn -Denv=test <phase>

Правильный параметр командной строки можно получить, просто заменив “-“ в идентификаторе профиля “=”.