zoukankan      html  css  js  c++  java
  • Java Build Practice 3:Ant Task

    Ant Task

    一 Task的命令行参数

    有些task可接受参数,并将其传递给另一个进程。为了能在变量中包含空格字符,可使用嵌套的arg元素。

    Attribute Description Required

    value 一个命令行变量;可包含空格字符。只能用一个

    line 空格分隔的命令行变量列表。

    file 作为命令行变量的文件名;会被文件的绝对名替代。

    path 一个作为单个命令行变量的path-like的字符串;或作为分隔符,Ant会将其转变为特定平台的分隔符。

    例子

    <arg value="-l -a"/> 是一个含有空格的单个的命令行变量。

    <arg line="-l -a"/> 是两个空格分隔的命令行变量。

    <arg path="/dir;/dir2:\dir3"/> 是一个命令行变量,其值在DOS系统上为\dir;\dir2;\dir3;在Unix系统上为/dir:/dir2:/dir3 。

    二 时间戳<tstamp/>

    在生成环境中使用当前时间和日期,以某种方式标记某个生成任务的输出,以便记录它是何时生成的,这经常是可取的。这可能涉及编辑一个文件,以便插入一个字符串来指定日期和时间,或将这个信息合并到 JAR 或 zip 文件的文件名中。

    这种需要是通过tstamp 任务来解决的。这个任务通常在某次生成过程开始时调用,比如在一个 init 目标中。这个任务不需要属性,许多情况下只需 <tstamp/> 就足够了。

    tstamp 不产生任何输出;相反,它根据当前系统时间和日期设置 Ant 属性。下面是 tstamp 设置的一些属性、对每个属性的说明,以及这些属性可被设置到的值的例子:

    属性说明例子

    DSTAMP 设置为当前日期,默认格式为yyyymmdd 20031217

    TSTAMP 设置为当前时间,默认格式为 hhmm 1603

    TODAY 设置为当前日期,带完整的月份2003 年 12 月 17 日

    例如,在前一小节中,我们按如下方式创建了一个 JAR 文件:

    <jar destfile="package.jar" basedir="classes"/>

    在调用 tstamp 任务之后,我们能够根据日期命名该 JAR 文件,如下所示:

    <jar destfile="package-{DSTAMP}.jar" basedir="classes"/>

    因此,如果这个任务在 2003 年 12 月 17 日调用,该 JAR 文件将被命名为 package-20031217.jar。

    还可以配置 tstamp 任务来设置不同的属性,应用一个当前时间之前或之后的时间偏移,或以不同的方式格式化该字符串。所有这些都是使用一个嵌套的 format 元素来完成的,如下所示:

    <tstamp>

    <format property="OFFSET_TIME"

    pattern="HH:mm:ss"

    offset="10" unit="minute"/>

    </tstamp>

    上面的清单将 OFFSET_TIME 属性设置为距离当前时间 10 分钟之后的小时数、分钟数和秒数。

    用于定义格式字符串的字符与 java.text.SimpleDateFormat 类所定义的那些格式字符相同 。

    三 发送email的task

    使用SMTP服务器发送邮件

    例子:

    <mail mailhost="smtp.myisp.com" mailport="1025" subject="Test build">

    <from address="me@myisp.com"/>

    <to address="all@xyz.com"/>

    <message>The {buildname} nightly build has completed</message>

    <fileset dir="dist">

    <includes name="**/*.zip"/>

    </fileset>

    </mail>

    四 ssh和scp

    在ant中,使用ssh命令远程启动和停止另外一台机器上的tomcat

    下面是远程停止192.168.0.2这台机器上的tomcat:
    <target name="sshexecstop" >
    < sshexec host="192.168.0.2" username="root" password="123456" trust="true" command="cd /usr/local/tomcat/bin;./shutdown.sh" />
    < /target>

    下面是远程启动192.168.0.2这台机器上的tomcat:

    <target name="sshexecstart">

    <sshexec host="192.168.0.2" username="root" password="123456" trust="true" command="cd /usr/local/tomcat/bin;./startup.sh" />

    </target>

    在ant中,使用scp将本地的文件(appwar目录下的文件)远程拷贝到另外一台机器的tomcat下:
    <target name="copy" >
    < scp todir="root:123456@192.168.0.2:/usr/local/tomcat/webapps/" trust="true">
    < fileset dir="/home/cruisecontrol/projects/eholderWeb/build/appwar" />
    < /scp>
    < /target>

    六 exec

    执行其他进程的task

    <exec executable="cmd.exe">
    <arg line="/c start http://localhost:8080>
    </exec>

    七其他的task

    if task
    ant原来可以在target级进行if判断(unless,if 属性),但实在太不方便了。
    Conditions
    但Ant预先封装的一堆condition很是很方便的。这些condition完全从实际出发,包括文件是否存在,http://localhost:8080是否连通都可以作为条件,见Ant的参考手册。
    For task
    支持"a,b,c,d" 字符串数组的循环与文件目录,Fileset的循环。
    Parallel task
    Parallel非常有用,比如我想一边开tomcat,一边做别的,就需要使用它,否则就只有用spawn=true属性把tomcat放在后台运行。spawn有很多不好的地方,比如不能即时在console看到信息,停止ant运行不能把tomcat关掉等。
    Parallel相当于一个容器,放在里面的每个task都会被并行执行。如果想把某几个task顺序执行,用相当于()的Sequential task 包起来。
    Waitfor task
    暂停ant执行直到条件符合,比如<waitfor><http url=http://localhost:8080/></waitfor>就会等待tomcat启动后才会继续往下执行。

    Ant流程控制 if elseif else

        使用循环,判断等控制时需添加ant-contrib包。

        taskdef定义第三方的任务,主要包含ant的处理流程逻辑

        if elseif else格式如下

    <property name="hello" value="false"/>
    <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpath="D:\Program Files\apache-ant-1.8.4\lib\ant-contrib-1.0b3.jar" />  
        <target name="run" depends="compile">
            <java classname="HelloWorld">
                <classpath path="${classdir}"/>
            </java>
            <if>
                <equals arg1="${hello}" arg2="true"/>
                <then>
                    <echo>${hello} is true</echo>
                </then>
                <elseif>
                    <equals arg1="${hello}" arg2="false"/>
                    <then>
                        <echo>${hello} is false</echo>
                    </then>
                </elseif>
                <else>
                    <echo>${hello}</echo>
                </else>
            </if>
       </target>

    八 antcall 与ant

    depends:depends中的targets在本target执行前按照从左到右的定义顺序调用。

    antcall : 用来调用同一个build.xml中的其他的target,相当于高级语言中的函数调用。

    ant: 调用其他的build.xml中的target。

    九 自定义TASK的使用

    ANT已经内置了不少task,像copy、replace、javac等等,但是有时候还需要一些特定的任务来完成操作,比如在生成JAD文件 时,需要一个Midlet-Jar-Size的参数,得到JAR文件的大小,但是通过内部的task无法实现,因此可以自己编写类来实现此功能,但必须保 证该类是从Task类继承过来的。

    例:

    <taskdef name="filesize" classname="ant.FileSizeTask" classpath="${LIB_PATH}/FileSizeTask.jar" />

    <filesize file="${Base}/Demo_Build/${jarName}" property="size" />

    <replace dir="store" includes="${jadName}" encoding="UTF-8">

    <replacefilter token="@FILESIZE@" value="${size}" />

    </replace>

    设置Property

    <!--设置属性name-value-->
    <property name="foo.dist" value="dist"/>
    <!--读取属性文件中的属性配置-->
    <property file="foo.properties"/>
    <!--读取网络中的property-set-->
    <property url="http://www.mysite/props/foo.properties"/>
    <!--读取文件中的属性配置-->
    <property resource="foo.properties"/>
    <!--读取环境变量-->
    <property environment="env"/>

    Using Length

    <!--把字符串foo的长度保存到属性length.foo中-->
    <length string="foo" property="length.foo"/>
    <!--把文件bar的长度保存到属性length.bar-->
    <length file="bar" property="length.bar"/>

    输出信息:

    <echo message="XXX"/>
    <echo>XXX</echo>

    引入另一个xml文件

    <import file="../common-targets.xml"/>

     

    显示错误信息

    Fail task 退出当前构建,抛出BuildException,打印错误信息。

    message:A message giving further information on why the build exited

    if:Only fail if a property of the given name exists in the current project

    unless:Only fail if a property of the given name doesn't exist in the current project

    status:Exit using the specified status code; assuming the generated Exception is not caught, the JVM will exit with this status.Since Apache Ant 1.6.2

    <fail>Something wrong here.</fail>
    <fail message="${属性}"/>
    <!--如果这个属性不存在,显示错误-->
    <fail unless="failCondition" message="unless Condition"/>
    <!--如果这个属性存在,显示错误-->
    <fail if="failCondition" message="if Condition"/>
    <!--如果符合条件,显示错误-->
    <fial message="tag condition">
        <condition>
            <not>
                <isset property="failCondition"/>
            </not>
        </condition>
    </fail>

    文件和目录处理

    拷贝文件

    <copy file="myfile.txt" tofile="mycopy.txt"/>
    <copy file="myfile.txt" todir="../otherdir"/>
    <copy todir="..//newdir">
        <fileset dir="src_dir"/>
    </copy>
    <copy todir="../destdir">
        <fileset dir="src_dir">
            <exclude name="**/*.java"/>
        </fileset>
    </copy>
    <copy todir="../destdir">
        <fileset dir="src_dir" excludes="**/*.java"/>
    </copy>
    <!--拷贝一个文件集合到一个目录,同时建立备份文件-->
    <copy todir="../backup/dir">
        <fileset dir="src_dir"/>
        <globmapper from="*" to="*bak"/>
    </copy>
    <!--拷贝一个集合的文件到指定目录,并替换掉TITLE-->
    <copy todir="../backup/dir">
        <fileset dir="src_dir"/>
        <filterset>
            <filter token="TITLE" value="Foo Bar"/>
        </filterset>
    </copy>
    <copydir src="${src}/resources" dest="${dest}" includes="**/*.java" excludes="**/Test.java"/>
    <copyfile src="test.java" dest="subdir/test.java"/>

    删除文件,目录

    <delete file="/lib/ant.jar"/>
    <delete dir="/lib"/>
    <delete>
        <fileset dir="." includes="**/*.bak"/>
    </delete>
    <!--删除当前目录下所有的文件和目录,包括当前目录-->
    <delete includeEmptyDirs="true">
        <fileset dir="build"/>
    </delete>
    <!--删除当前目录下所有的文件和目录,不包括当前目录-->
    <delete includeEmptyDirs="true">
        <fileset dir="build" includes="**/*"/>
    </delete>
    <!--删除当前目录下所有的svn相关文件(因为SVN文件默认是excludes的,所以这里说明一下)-->
    <delete defaultExcludes="false">
        <fileset dir="${src}" includes="**/*.svn"/>
    </delete>
    <!--删除文件目录树-->
    <deltree dir="dist"/>

    建立目录

    <mkdir dir="${dist}/lib"/>

    剪切文件

    <move todir="some/new/dir">
        <fileset dir="my/src/dir">
            <include name="**/*.jar"/>
            <exclude name="**/ant.jar"/>
        </fileset>
    </move>

    重命名

    <rename src="foo.jar" dest="ant-${version}.jar"/>

    建立临时文件

    <!--在目录build下,建立文件名为temp.file,后缀名为xml的文件-->
    <tempfile property="temp.file" destDir="build" suffix=".xml"/>

    Touch使用

    <!--如果文件不存在,创建文件,如果存在,更改最后访问时间为当前系统时间-->
    <touch file="myfile"/>
    <!--如果文件不存在,创建文件,更改最后访问时间-->
    <touch file="myfile" datetime="06/28/2000 2:02 pm"/>
    <!--更改目录下所有文件的最后访问时间-->
    <touch datetime="09/10/1974 4:30 pm">
        <fileset dir="src_dir"/>
    </touch>

    Ant中使用文件的压缩

    <?xml version="1.0"?>
    
    <project name="access" default="show" basedir=".">
        <buildnumber/>
    
        <target name="show">
            <echo message="${build.number}"/>
            <tar destfile="${build.number}.tar.bz2" compression="bzip2">
                <fileset dir=".">
                    <include name="*.xml"/>
                </fileset>
            </tar>
    
            <untar src="${build.number}.tar.bz2" compression="bzip2" dest="dest" />
        </target>
    </project>

    buildnumber: 每次运行的时候,ant都会将该值进行增加并写到一个build.number文档中(在basedir下面).当然,你也可以使用file属性指定到某个文件中.如下

    <buildnumber file="my.properties"/>

    如果在运行时,ant在指定(或者默认)的文件中找不到build.number值,它会重新生成该值,并从0开始增长.

    tar是Linux平台上的打包命令. 在Linux上,文件压缩命令如zip,gzip,bzip2等,都只能针对单独的文件进行,你不能对文件或多个文件进行压缩. 为了让多个文件形成一个整体,就需要使用tar命令. tar会将零散的多个文件连接成一个整体,但是默认的情况下,它是不会压缩这些零散的文件的。也就是说tar命令的产生就是为也形成一个整体文件(名副其实的打包命令),然后再供gzip、bzip2之类的文件压缩命令使用。在tar后来的发展过程中,为了方便,tar有了一些文件压缩的属性选项。ant的tar就是模仿linux上tar命令。你可以加一个compression属性对文件进行压缩。具体的打包和解包如上。

    <!--解压缩zip文件-->
    <unzip src="apache-ant-bin.zip" dest="${tools.home}">
        <patternset>    
            <include name="apache-ant/lib/ant.jar"/>
        </patternset>
        <mapper type="flatten"/>
    </unzip>
    
    <!--压缩zip文件-->
    <zip destfile="${dist}/manual.zip" basedir="htdoc/manual" includes="api/**/*.html" excludes="**/todo.html"/>
    
    <!--打tar包-->
    <tar destfile="/Users/antoine/dev/asf/ant-core/docs.tar">
        <tarfileset dir="${dir.src}/doc" fullpath="/usr/doc/ant/README" preserveLeadingSlashes="true">
            <include name="readme.txt"/>
        <tarfileset>
        <tarfileset dir="${dir.src}/docs" prefix="/usr/doc/ant" preserveLeadingSlashes="true">
            <include name="*.html"/>
        </tarfileset>
    </tar>
    
    <!--解压tar包-->
    <untar src="tools.tar" dest="${tools.home}"/>

    Ant中过滤器的使用

    <?xml version="1.0"?>
    
    <project name="access" default="show" basedir=".">
        <!--
            将一组需要过滤的值写入一个过滤文件,过滤文件的
            格式与一般的属性文件相同,如下:
            month=12
            year=2008
        -->
        <filter filtersfile="filter.properties"/> 
    
        <!--
            定义一个过滤器
        -->
        <filter token="time" value="14时7分"/>
    
        <target name="show">
            <mkdir dir="dest"/>
            <!-- 在copy中添加filtering属性启动过滤器 -->
            <copy todir="dest" filtering="true">
                <fileset dir="src"/>
            </copy>
        </target>
    </project>

    该过滤器的作用就是将src下面的文件复制到dest目录下面.而且将src下面,凡是包含@year@,@month@,@time@的字符进行替换(是指src文件夹下面文件中的内容,不是指文件名)。另外还可以使用filterset标签,它也可以引用外部filter文件,也可以在内容指定一组filter,如下:

    与单纯地使用filter相比,filterset的功能要强大一点,使用得被替换的字符不仅限制于以@开始和结束的变量了,你可以自己定义(使用begintoken和endtoken)。

    <copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
      <filterset>
        <filter token="DATE" value="${TODAY}"/>
      </filterset>
    </copy>
    
    <!-- 自定义变量的格式 -->
    <copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
      <filterset begintoken="%" endtoken="*">
        <filter token="DATE" value="${TODAY}"/>
      </filterset>
    </copy>
    
    <!-- 使用外部的过滤定义文件 -->
    <copy toDir="${dist.dir}/docs">
      <fileset dir="${build.dir}/docs">
        <include name="**/*.html">
      </fileset>
      <filterset begintoken="%" endtoken="*">
        <filtersfile file="${user.dir}/dist.properties"/>
      </filterset>
    </copy>
    
    <!-- 使用引用方式,重复利用过滤集 -->
    <filterset id="myFilterSet" begintoken="%" endtoken="*">
      <filter token="DATE" value="${TODAY}"/>
    </filterset>
    
    <copy file="${build.dir}/version.txt" toFile="${dist.dir}/version.txt">
      <filterset refid="myFilterSet"/>
    </copy>

    Ant交互

    Ant脚本在运行过程中可以与操作人员进行交互。交互时使用到的主要几个标签如下:

    <input
        message="All data is going to be deleted from DB continue (y/n)?"
        validargs="y,n"
        addproperty="do.delete" defaultvalue="n"
      />
    <condition property="do.abort">
        <equals arg1="n" arg2="${do.delete}"/>  </condition>
      <fail if="do.abort">Build aborted by user.</fail>

    input :是指输入,message - 显示给用户的提示;validargs - 只有在用户输入validargs指定的值时,输入才是有效的。addproperty - 用户在完成一次有效的输入后,会产生一个属性,该属性的名称为addproperty的值,属性的值则为用户的输入。如果用户不做输入操作(如直接回车),则属性的值将会是defaultvalue的值。

    condition:是对条件进行判断,条件判断完成后,也会产生一个属性值。属性名称为property的值,而属性的值则由condition的内部标签返回值决定,默认为ture|false,但是不仅限于这两个值,如下情况:

    <condition property="do.delete" value="yes" else="no">
        <equals arg1="n" arg2="${input.value}"/>
    </condition>
    <!-- 
        当用户的输入为n是,equals返回为ture,此是,do.delete的值会被设置为“yes”(该值由value属性指定),否则do.delete的值为被设置为no(由else属性的值指定)。也就是说,condition产生的属性的值并不仅限于true和false
     -->

    equals:等于判断。它有两个属性,arg1和arg2,不用说,就是判断这两个属性的值是否相等了。

    此外,condition还支持其它类似的判断操作符如and、or、available、isset、istrue、isfalse、contains等等(详见http://ant.apache.org/manual/CoreTasks/conditions.html)。其中有几个需要重点讲一下(个人觉得功能还是比较有用的):

    正则表达式的应用(具体如何使用正则表达式这里就不说了,强大但复杂,可以写一部新华字典出来)

    Condition的使用 <and> <or> <not>等tag

    peoperty: The name of the property to set

    value: The value to set the property to.

    else: The value to set the property to if the codition evaluates to false.

    <condition property="isMacOsButNotMacOsX">
        <and>
            <os family="mac">
            <not>
                <os family="unix">
            </not>
        </and>
    </condition>

    1.matches:先看个例子

    <condition property="legal-password">
      <matches pattern="[1-9]" string="${user-input}"/>
    </condition>
    <fail message="Your password should at least contain one number"
          unless="legal-password"/>

    其中呢,pattern就是正则表达式了,而string就是需要进行匹配的字符窜。而它还有几个有用的属性

    casesensitive(true|false):是否区分大小写(默认为true);
    singleline(true|false):单行模式,此时“.”将可以匹配换行符(默认为false);
    multiline(true|false):多行模式(默认为false);

    另外,在使用正则表达式时,ant还支持复用(可省很多事)。如下:

    <regexp id="date.pattern" pattern="^[0123]\d-[01]\d-[12]\d\d\d$"/>
    
    <condition property="is_expected">
      <matches string="${today}">
        <regexp refid="date.pattern"/>
      </matches>
    </condition>
    <!-- 如上,我们就可以重复地利用一段正式表达式了,就像声明了一个变量一样 -->

    2.antversion

    这个标签是指定ant的使用版本。为也避免使用过程中产生的不兼容性(如果把jdk1.6的代码在jdk1.4上运行时没有任何异常提示,也没有任何运行结果,你肯定很不爽),所以大家写脚本时,也尽量指定一个ant的版本需求。

    antversion有两个属性:
    atleast(major.minor.point):在某个版本以上(如1.7.0,通常应该是你当前脚本的运行版本)都可以正常使用;
    exactly(major.minor.point):只能在这个指定的版本上运行(要求很苛刻呀,可能是这个版本上有特别的功能,而刚好其前后的版本都没有)。

    下面是一个例子:

    <condition property="ant-is-exact-7">
      <antversion exactly="1.7.0"/>
    </condition>
    
    <fail unless="${ant-is-exact-7}" message="您使用的Ant版本不符合要求"/>
     
    替换
    <replace 
        file="configure.sh"
        value="defaultvalue"
        propertyFile="src/name.properties">
      <replacefilter 
        token="@token1@"/>
      <replacefilter 
        token="@token2@" 
        value="value2"/>
      <replacefilter 
        token="@token3@" 
        property="property.key"/>
      <replacefilter>
        <replacetoken>@token4@</replacetoken> 
        <replacevalue>value4</replacevalue>
      </replacefilter>
    </replace>
     

    Ant中的classpath

    <classpath>
      <pathelement path="${classpath}"/>
      <fileset dir="lib">
    	<include name="**/*.jar"/>
      </fileset>
      <pathelement location="classes"/>
      <dirset dir="${build.dir}">
    	<include name="apps/**/classes"/>
    	<exclude name="apps/**/*Test*"/>
      </dirset>
      <filelist refid="third-party_jars"/>
    </classpath>
    对于classpath标签的两个属性——path和location:path指定jar包,location指定包含jar包的路径
    按照官网上的解释:
    location的值可以是一个文件(file),也可以是一个相对于当前根目录(project的basedir)的文件夹(directory),或者是一个带有绝对路径的文件(文件夹)
    path则表示的是一系列的用分号(“;”)或冒号(“:”)分隔开的location值。
    当然,也可以如上所示,在classpath标签中使用fileset、dirset,这样省事不少。
     
    使用classpath:
    <target>
        <javac>
            <classpath refid="project.class.path"/>
        </javac>
    </target>

    在上面的代码中还提到了一个filelist标签。它代表了一组文件,相当于一个文件集合。但是这个文件集合中的文件并不是一定会存在。filelist的使用方式如下:
    <filelist 
        id="docfiles" 
        dir="${doc.src}"
        files="foo.xml,bar.xml"/> 
    
    <filelist 
        id="docfiles" 
        dir="${doc.src}">
        <file name="foo.xml"/>
        <file name="bar.xml"/>
    </filelist>
    
    <!-- 其中的${doc.src}/foo.xml,${doc.src}/bar.xml并不一定会存在地 -->

     编译和打包

    package jar

    <jar destfile="${dist}/lib/app.jar" basedir="${build}/classes"/>
    <jar destfile="${build}/lib/app.jar" basedir="${build}/classes" includes="mypackage/test/**" excludes="**/Test.class"/>

    package ear

    <ear destfile="build/myapp.ear" appxml="src/metadata/application.xml">
        <fileset dir="build" includes="*.jar,*war"/>
    </ear>

    compile source program

    <javac srcdir="${src}" destdit="${build}" classpath="xyz.jar" debug="on" source="1.4"/>

    running jar

    <java classname="test.Main" dir="${exec.dir}" jar="${exec.dir}/dist/test.jar" fork="true" failonerror="true" maxmemory="128m">
        <arg value="-h"/>
        <classpath>
            <pathelement location="dist/test.jar"/>
            <pathelement path="/Users/antoine/dev/asf/ant-core/bootstrap/lib/ant-launcher.jar"/>
        </classpath>
    </java>

    package war

    <war destfile="myapp.war" webxml="src/metadata/myapp.xml">
        <fileset dir="src/html/myapp"/>
        <fileset dir="src/jsp/myapp"/>
        <lib dir="thirdparty/libs">
            <exclude name="jdbc1.jar">
        </lib>
        <classes dir="build/main"/>
        <zipfileset dir="src/graphics/images/gifs" prefix="images"/>
    </war>

    调用chmod

    <chmod perm="go-rwx" type="file">
        <fileset dir="/web">
            <include name="**/*.cgi"/>
            <include name="**/*.old"/>
        </fileset>
        <dirset dir="/web">
            <include name="**/private_*"/>
        </dirset>
    </chmod>
  • 相关阅读:
    hihoCoder 1398 : 网络流五·最大权闭合子图
    hihoCoder:1394 : 网络流四·最小路径覆盖
    hihoCoder 1393: 网络流三·二分图多重匹配
    hihoCoder1378:网络流二·最大流最小割定理
    hihoCoder1369:网络流一·Ford-Fulkerson算法(FF算法)
    [NOIP2011]铺地毯(贪心)
    hdu 3452:Bonsai(最小割)
    hdu 3549:Flow Problem(最大流)
    (转载)JavaScript中定义变量
    (转载)浅谈javascript的分号
  • 原文地址:https://www.cnblogs.com/yefengmeander/p/2950648.html
Copyright © 2011-2022 走看看