Задача ANT — несколько файлов — зацикливание

У меня довольно сложная задача муравья написать. Пришлось делать отдельные файлы билдов и 1 остался. Есть 4 файла:

 buildpackage.bat, buildpackage.xml, buildpackage.txt, structure.xml

buildpackage.bat — это просто файл ant, который вызывает buildpackage.xml "ant -f buildpackage.xml". Это нормально.

buildpackage.txt прост и содержит строки, разделенные запятыми:

server1,client1
server2,client2
genericserver,client[x]
server[x],client[x]

Всегда есть имя сервера и имя клиента:

  • Server name может состоять только из букв, цифр и букв, но имеет переменную длину.

  • Client name - только буквы.

Мне нужно разделить эти файлы на 2 отдельные переменные: server.name и client.name.

Что я сделал:

<loadfile property="buildpackage" srcFile="buildpackage.txt"/>    
<for list="${buildpackage}" param="depl.det" delimiter="${line.separator}">
 <sequential>
  <echo>
   Input:   @{deploy.details}
  </echo>
 </sequential>
</for>

Результаты, которые я получаю:

[echo] Input:   server_01,Client_01
[echo] Input:   server_02,Client_02
[echo] Input:   server_03,Client_03
[echo] Input:   clientserver,client_04

Я бы хотел, чтобы server_01 был параметром 1, а client_02 — параметром 2.

Я буду выполнять другие задачи в последовательности. Я скопирую файлы из каталога client[x] на server[x] (это просто, но как мне разделить эту строку на 2 отдельные переменные?).

А теперь очень сложная задача.

У меня также есть файлstructure.xml. Структура:

 <core>
   <clients>
     <client>
       <name>Client_01</name>
       <branding>brand_01</branding>
       <applications>
         <application>application_01</application>
       </applications>
       <solutions>
         <solution>solution01</solution>
       </solutions>
   </client>
   <client>
     <name>Client_07</name>
     <branding>brand_09</branding>
     <applications>
       <application>application_03</application>
     </applications>
     <solutions>
       <solution>solution01</solution>
       <solution>solution07</solution>
       <solution>solution08</solution>
     </solutions>
   </client>
  <clients>
 <core>

У меня много клиентов, приложений, брендов, решений. У каждого клиента может быть только 1 приложение и 1 брендинг, но несколько решений.

Основываясь на предыдущей последовательной задаче (получить сервер и имя), я должен просмотреть этот XML-файл и получить для каждого соответствующего клиента (из предыдущей задачи)

- branding
- application 
- solutions (multiple or one) 

Я могу читать XML с помощью <xmlproperty>, но я не знаю, как сделать это последовательно (итерировать) и получить соответствующие свойства.

Я знаю, что это сложно. Резюме:

  1. скрипт сборки запускается
  2. скрипт сборки извлекает данные из buildpackage.txt как client.name и server.name
  3. Сценарий сборки получает ${client.name} из шага 2 в качестве параметра и циклически обрабатывает файлstructure.xml. Он находит подходящие бренды/приложения/решения и выводит их для каждого подходящего клиента.

Выход:

server.name
client.name
branding.name
application name
solution [X]
solution [y]

Ant должен обрабатываться для каждого сервера/клиента. Я могу сделать остальную часть сборки и скомпилировать, если у меня есть свойства. Я не могу трогать структуру.xml. Я могу сделать все, что мне нужно/хочу, для buildpackage.cml и buildpackge.txt. Buildpackage.txt можно заменить файлом .xml или любым другим файлом (например, .properties). Он должен содержать эти два значения server и client.

Спасибо за помощь.

РЕДАКТИРОВАТЬ

Теперь у меня проблема. Если я помещаю одно и то же приложение 2 раза в файл сборки, оно создает только первое. Вот мой файл build.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="DoTheJob" basedir="." xmlns:ac="antlib:net.sf.antcontrib" xmlns:if="ant:if" xmlns:unless="ant:unless">
    <!-- Define classpath and default locations-->
    <property name="root.dir" location="../.."/>
    <property name="dest.dir" location="packages"/>
    <property name="ant.lib.dir" location="../lib"/>
    <property name="repository_url" value="http://repository-url"/> 
    <path id="project.classpath">
        <fileset dir="${ant.lib.dir}">
        <include name="*.jar"/>
        </fileset>
        <fileset dir="${root.dir}/IIT/lib">
            <include name="*.jar"/>
        </fileset>
        <pathelement location="../savedjar/compiled.jar"/>
    </path>
    <path id="svnant.path">
        <fileset dir="../lib/">
            <include name="svnant.jar"/>
            <include name="svnClientAdapter.jar"/>
        </fileset>
    </path>
    <typedef resource="org/tigris/subversion/svnant/svnantlib.xml" classpathref="svnant.path" /> 
    <tstamp>
        <format property="build_time" pattern="YYYY MMMM dd HH:mm:ss"/>
    </tstamp>   
    <!-- Change characters to lowercase -->
    <scriptdef language="javascript" name="lowercase">
        <attribute name="string" />
        <attribute name="to" />
        project.setProperty(attributes.get("to"),attributes.get("string").toLowerCase());
    </scriptdef>
    <!-- Import XMLTask for XML transformations-->
    <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask" classpathref="project.classpath"/>
    <!-- Define buildfiles locations-->
    <property name="structure.file" location="../structure.xml"/>
    <property name="buildpackage.file" location="buildpackage.txt"/>
    <!-- load client/server file-->
    <property file="${buildpackage.file}"/>
    <!-- loop over all clients --> 
    <xmltask source="${structure.file}">
    <call path="//client">
        <param path="name/text()" name="client"/>
        <param path="branding/text()" name="branding"/>
        <param path="applications/application/text()" name="application"/>
            <actions>
                <!-- loop through all XML properties only when @{client} condition is matched --> 
                <ac:if>
                    <isset property="@{client}"/>
                    <then>
                        <echo>=====================================================</echo>
                        <echo>Building package for @{client} on ${@{client}}       </echo>
                        <echo>=====================================================</echo>
                        <!-- creating destination directories -->                           
                        <delete dir="${build.dir}/@{client}"/>
                        <mkdir dir="${dest.dir}/@{client}"/> 
                        <echo>=====================================================</echo>
                        <echo>Copy application files code/temaplates to destination</echo>
                        <echo>=====================================================</echo>                      
                        <!-- creating code build directory -->
                        <mkdir dir="${dest.dir}/@{client}/code"/> 
                        <!-- copy application code --> 
                        <echo>copy application code</echo>
                        <copy todir="${dest.dir}/@{client}/code/application/@{application}" overwrite="yes">
                            <fileset dir="${root.dir}/client-source/application/@{application}" includes="**/*.*"/>
                        </copy>
                        <!-- copy application templates --> 
                        <mkdir dir="${dest.dir}/@{client}/app/@{client}"/> 
                        <echo>copy application templates</echo>
                        <copy todir="${dest.dir}/@{client}/app/@{client}" overwrite="yes">
                            <fileset dir="${root.dir}/applications/apps/@{application}" includes="**/*.*"/>
                        </copy>
                        <echo>===================================================</echo>
                        <echo>copy Solutions files code/temaplates to destination</echo>
                        <echo>===================================================</echo>    
                        <!-- nested xmltask for loop over solutions -->
                        <xmltask source="${structure.file}">
                            <call path="/core/clients/client[name/text()='@{client}']/*/solution">
                                <param path="text()" name="solution"/>
                                    <actions>
                                        <!-- making sure that solution is lowercase -->
                                        <lowercase string="@{solution}" to="solution.lowercase" />
                                        <echo>copy solutions code</echo>
                                        <!-- copy solutions code -->
                                        <copy todir="${dest.dir}/@{client}/code/solutions/${solution.lowercase}" overwrite="yes">
                                            <fileset dir="${root.dir}/client-source/solutions/${solution.lowercase}" includes="**/*.*"/>
                                        </copy>
                                        <!-- copy solutions templates -->
                                        <echo>copy Solutions templates</echo>
                                        <copy todir="${dest.dir}/@{client}/app/@{client}" overwrite="yes">
                                            <fileset dir="${root.dir}/applications/solutions/${solution.lowercase}" includes="**/*.*"/>
                                        </copy>
                                    </actions>
                            </call>
                        </xmltask>
                        <echo>==============================================</echo>
                        <echo>copy Client files code/temaplates to destination</echo>
                        <echo>==============================================</echo> 
                        <!-- copy branding directory --> 
                        <copy todir="${dest.dir}/@{client}/app/@{client}" overwrite="yes">
                            <fileset dir="${root.dir}/applications/brandings/@{branding}" includes="**/*.*"/>
                        </copy>                         
                        <!-- copy client code --> 
                        <copy todir="${dest.dir}/@{client}/code/client/@{client}" overwrite="yes">
                            <fileset dir="${root.dir}/client-source/client/@{client}" includes="**/*.*"/>
                        </copy>                         
                        <!-- set templates directory --> 
                        <copy todir="${dest.dir}/@{client}/app/@{client}" overwrite="yes">
                            <fileset dir="${root.dir}/applications/clients/@{client}" includes="**/*.*"/>
                        </copy>         
                        <echo>==============================================</echo>
                        <echo>compiling code                                </echo>
                        <echo>==============================================</echo> 
                        <!-- making classes directory --> 
                        <mkdir dir="${dest.dir}/@{client}/classes"/>
                        <javac destdir="${dest.dir}/@{client}/classes" classpathref="project.classpath" debug="on" fork="true" includeantruntime="false" memoryinitialsize="256m" memorymaximumsize="1024m">
                            <src path="${dest.dir}/@{client}/code/"/>
                        </javac>
                        <echo>==============================================</echo>
                        <echo>creating Client.jar                           </echo>
                        <echo>==============================================</echo> 
                        <!-- jar classes directory --> 
                        <jar jarfile="${dest.dir}/@{client}/app/Client.jar" basedir="${dest.dir}/@{client}/classes"/>
                        <!-- reaplce text in files --> 
                        <replaceregexp byline="true">
                            <regexp pattern="@ReleaseDate@"/>
                            <substitution expression="${build_time}"/>
                            <fileset dir="${dest.dir}/@{client}/app/@{client}/templates/">
                                <include name="*.htm"/>
                                <include name="*.html"/>            
                                <include name="*.ini"/>
                            </fileset>
                        </replaceregexp>
                        <!-- get svn info --> 
                        <svn username="********" password="************" javahl="false" svnkit="false">
                            <info target="${repository_url}" />
                        </svn>
                        <!-- creating Client.ini file --> 
                        <touch file="${dest.dir}/@{client}/app/Client.ini"/>
                        <concat destfile="${dest.dir}/@{client}/app/Client.ini" append="true">releaseDate=${build_time}${line.separator}</concat>
                        <concat destfile="${dest.dir}/@{client}/app/Client.ini" append="true">svnrevision=${svn.info.lastRev}${line.separator}</concat>
                        <!-- ziping package --> 
                        <zip destfile="${dest.dir}/@{client}/@{client}.zip" basedir="${dest.dir}/@{client}/app/" />
                        <echo>==============================================</echo>
                        <echo>sending @{client}.zip to the server via scp      </echo>
                        <echo>==============================================</echo> 
                        <!-- scp file to remote server -->
                        <property name="scp_username" value="**********"/>
                        <property name="scp_password" value="**********"/>
                        <property name="scp_server" value="${@{client}}"/>
                        <property name="scp_remote_directory" value="*********"/>
                        <scp trust="true" verbose="true" localFile="${dest.dir}/@{client}/@{client}.zip" remoteTodir="${scp_username}:${scp_password}@${scp_server}:${scp_remote_directory}"/>  
                        <echo>${line.separator}${line.separator}${line.separator}</echo>
                    </then>
                </ac:if> 
            </actions>
        </call>
    </xmltask>
</project>

И если я попытаюсь создать файл buildpackage.txt

client1=server1
client2=server1
client1=server2

Я получаю клиент1-> сервер1, клиент2-> сервер1, но ...... нет клиента 1 на сервер2

РЕДАКТИРОВАТЬ2

Все исправлено. Решение задачи scp:

       <!-- scp file to remote server -->
        <property name="scp_username" value="XXX"/>
        <property name="scp_password" value="YYY"/>
        <property name="scp_remote_directory" value="path"/>
        <for list="${@{client}}" delimiter="=" param = "val">
            <sequential>
            <property name="token" value="@"/>
            <scp trust="true" verbose="true" localFile="${dest.dir}/@{client}/@{client}.zip" remoteTodir="${scp_username}:${scp_password}${token}@@{val}:${scp_remote_directory}"/> 
        </sequential>
        </for>

Все работает. Спасибо за помощь


person ES Su    schedule 24.02.2015    source источник


Ответы (1)


Для любой сборки на основе xml я рекомендую использовать надстройку ant xmltask, что-то вроде :
( предполагая, что имена клиентов похожи в buildpackage.txt иstructure.xml - ваш фрагмент имеет несколько имен Client_01, client1 .. !?)

<project>
 <!-- Import XMLTask -->
 <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>

 <!-- make buildpackage.txt a valid propertyfile to get the job done
      f.e. server1,client1 will get client1=server1 etc. ..
      to make server easily accessible within xml loop
 -->
 <replaceregexp
   file="buildpackage.txt"
   match="(.+),(.+)"
   replace="\2=\1"
   byline="true" />

 <!-- now load that file -->
 <property file="buildpackage.txt"/>

 <!-- loop over all clients --> 
 <xmltask source="structure.xml">
  <call path="//client">
   <param path="name/text()" name="client"/>
   <param path="branding/text()" name="branding"/>
   <param path="applications/application/text()" name="application"/>

   <!-- inside action adress params with @{..} syntax ! -->
   <actions>
    <!-- the copy (or whatever) action
         here demonstrated with echoxml -->
   <echoxml>
    <!-- access the corresponding server (set via buildpackage.txt)
         with nested ${@{..}} syntax ! -->
    <copy todir="//${@{client}}/share">
     <fileset dir="@{client}/somedir"/>
    </copy>
   </echoxml>

   <echo>${@{client}}${line.separator}@{client}${line.separator}@{application}${line.separator}@{branding}</echo>

   <!-- another nested xmltask for loop over solutions -->
   <xmltask source="structure.xml">
    <call path="//client[name/text()='@{client}']/*/solution">
     <param path="text()" name="solution"/>
      <actions>
       <echo>@{solution}${line.separator}</echo>
      </actions>
     </call>
   </xmltask>

   </actions>
  </call>
 </xmltask>
</project>

Если возможно, вы должны сразу же создать buildpackage.txt как действительный файл свойств
в формате => Client[x]=Server[x].
Тогда все, что вам нужно, это xmltask. Поместите все необходимые действия в раздел вложенных действий, как показано с помощью команды echoxml/copy.

Вывод:

<?xml version="1.0" encoding="UTF-8"?>
<copy todir="//server1/share">
  <fileset dir="client1/somedir" />
</copy>
     [echo] server1
     [echo] client1
     [echo] application_03
     [echo] brand_01
     [echo] solution01
<?xml version="1.0" encoding="UTF-8"?>
<copy todir="//server4/share">
  <fileset dir="client4/somedir" />
</copy>
     [echo] server4
     [echo] client4
     [echo] application_03
     [echo] brand_09
     [echo] solution01
     [echo] solution07
     [echo] solution08
BUILD SUCCESSFUL

-- ИЗМЕНИТЬ после вопроса об условном действии, только если установлен ${@{client}} --

если файл свойств buildpackage.txt содержит неправильные свойства, это означает, что сервер = клиент, а не клиент = сервер, например. :

Sol-e-45-D=PRODUCTION1
CONNECTION=PRODUCTION3
PRODUCTION4=DFHH
PRODUCTION5=MEGA-5

вам нужна логика if isset. Лучше всего использовать новую функцию if/unless (появившуюся в ant191), если вы re on ant >= 1.9.3, fe :

<!-- you need to activate that feature via namespaces -->
<project
  xmlns:if="ant:if"
  xmlns:unless="ant:unless"
>

 <!-- Import XMLTask -->
 <taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>

 <!-- load propertyfile with client=server mappings -->
 <property file="buildpackage.txt"/>

 <!-- loop over all clients -->
 <xmltask source="structure.xml">
  <call path="//client">
   <param path="name/text()" name="client"/>
   <param path="branding/text()" name="branding"/>
   <param path="applications/application/text()" name="application"/>

   <!-- inside action adress params with @{..} syntax ! -->
   <actions>
    <!-- the copy (or whatever) action
         here demonstrated with echoxml -->
   <echoxml if:set="@{client}">
    <!-- access the corresponding server (set via buildpackage.txt)
         with nested ${@{..}} syntax ! -->
    <copy todir="//${@{client}}/share">
     <fileset dir="@{client}/somedir"/>
    </copy>
   </echoxml>

   <echo if:set="@{client}">${@{client}}${line.separator}@{client}${line.separator}@{application}${line.separator}@{branding}</echo>

   <!-- another nested xmltask for loop over solutions -->
   <xmltask source="structure.xml">
    <call path="//client[name/text()='@{client}']/*/solution">
     <param path="text()" name="solution"/>
      <actions>
       <echo if:set="@{client}">@{solution}${line.separator}</echo>
      </actions>
     </call>
   </xmltask>

   </actions>
  </call>
 </xmltask>
</project>

Примечание => вызов xmltask также имеет атрибут if, но имя клиента неизвестно при запуске цикла, не может использовать <call path="//client" if="@{client}">
вывод:

<?xml version="1.0" encoding="UTF-8"?>
<copy todir="//PRODUCTION1/share">
  <fileset dir="Sol-e-45-D/somedir" />
</copy>
     [echo] PRODUCTION1
     [echo] Sol-e-45-D
     [echo] Sol-e-45-D
     [echo] Branding-BLUE
     [echo] Sol-e-45-D
<?xml version="1.0" encoding="UTF-8"?>
<copy todir="//PRODUCTION3/share">
  <fileset dir="CONNECTION/somedir" />
</copy>
     [echo] PRODUCTION3
     [echo] CONNECTION
     [echo] Sol-e-45-D
     [echo] Branding-BLUE
     [echo] Sol-e-45-D

В противном случае при привязке к ant ‹= 1.9.3 вы можете использовать antcontrib if task, так как этот taskdef уже есть в вашем фрагменте кода:

<if>
 <isset property="@{client}"/>
  <then>
   <!-- ... -->
  </then>
</if> 

Осторожно, ваше определение antcontrib taskdef неверно !!, antcontrib.properties содержит только taskdefs для ant ‹ 1.6, объявите его так (при условии, что antcontrib.jar уже находится в пути):

  <project xmlns:ac="antlib:net.sf.antcontrib">

    <ac:if>
    ...

или без пространств имен :

  <project>

  <!-- Import AntContrib -->
  <taskdef resource="net/sf/antcontrib/antlib.xml"/>
  ...
person Rebse    schedule 26.02.2015
comment
РЕДАКТИРОВАТЬ. теперь все работает отлично, но есть одна проблема. Если я хочу собрать application1=server1 и application1=server2 и appcation1=server3 - будет создано только одно приложение. Если я поставлю applicaiton1=server1, application2=server1, applicaiton1=server2 - stull создаст приложение 1 только один раз. - person ES Su; 18.03.2015
comment
@ES Су, у вас есть 3 свойства с именем application1. Это невозможно. В ant свойство, однажды установленное, является неизменным. Когда файл свойств загружается, первое определение, т.е. application1=server1 wins означает, что свойство будет иметь значение server1. - person Rebse; 18.03.2015
comment
Что если buildpackage.txt будет в формате: client1=server1=server2=server3 client2=server1 client3=server1=server2=server3=server4=server5 от одного до 5 серверов. Является ли это возможным? - person ES Su; 19.03.2015
comment
Нет, свойство java должно быть ключ = значение, но возможно что-то вроде ключ = значение1, значение2, значение3.... Затем используйте цикл для обработки значений свойств. - person Rebse; 19.03.2015
comment
Фиксированный. Решение: <property name="token" value="@"/> <scp trust="true" verbose="true" localFile="${dest.dir}/@{client}/@{client}.zip" remoteTodir="${scp_username}:${scp_password}${token}@@{val}:${scp_remote_directory}"/> <echo>${line.separator}${line.separator}${line.separator}</echo> </sequential> - person ES Su; 19.03.2015
comment
P.S. : чтобы уточнить мой последний комментарий .. на самом деле вы можете использовать такое свойство, как foo=bla=blaa=blaaa, что означает, что значение foo будет bla=blaa=blaaa; Это допустимое свойство, но оно необычно.. - person Rebse; 19.03.2015
comment
ПС: уже исправлено. Работает как оберег. Ребята довольны.....но теперь им надо все переделывать. А пока - будем иметь как есть и ни единой ошибки :). Спасибо большое - person ES Su; 19.03.2015
comment
Привет. Какой у меня есть файл, в котором у меня есть (несколько строк) client=war_name=server1+server2=repository=paramter_x=parameter_y. Можно ли разделить этот файл (в зависимости от разделителя) и проанализировать как параметр, или мне создать еще один вопрос о переполнении стека? - person ES Su; 30.03.2015
comment
@ES Су, лучше создай новый вопрос! - person Rebse; 30.03.2015