ant 与svn 【email,date-format】

本文详细介绍了构建自动化流程的关键步骤,包括使用版本控制工具、编译工具搭建环境,以及如何实现自动编译、代码走查、制作jar包、发送构建完成邮件等操作。通过构建流程的自动化,确保项目的高效稳定运行。
构建的定义:构建就是把源代码转换成class文件的过程

构建解决的问题:研发给的class进行部署,然后我们就会发现研发给的东东有许多错误;给的class文件不是最新的;更甚至一个项目需要多个工程进行合并才能运行.

为什么要构建:一般研发是通过IDE工具进行构建的,当研发给出的错误的class文件时,压根在测试中就发现不了问题;所以我们要寻求另一种途径来编译class文件.
构建的主流程:先整合在一起,然后进行编译,生产jar包,最后自动部署到测试机上。

搭建自动构建环境:
1 安装版本控制工具(cvs或svn)
2 编译工具(或构建工具,代码走查工具)
3 邮箱

下面以java 在windows环境下为例:
1 安装cvs(或svn)工具
2 安装ant
a)部署JDK环境,配置jdk环境变量
b)下载ant包,然后进行解压;配置环境变量
c)进入cmd,确定ant安装成功
d)ant的介绍:ant的核心是build.xml 文件 以下是一个简单的build.xml 文件

<?xml version="1.0" encoding="UTF-8" ?>

<project name="SVN Ant Test" default="start" basedir=".">

<taskdef resource="checkstyletask.properties" classpath="C:\Program Files\CruiseControl\apache-ant-1.7.0\lib\checkstyle-all-4.4.jar"/>

<property name="svn.user" value="yanshunhua"/>
<property name="svn.password" value="yanshunhua#6268"/>
<property name="svn.url" value="
http://192.168.0.85/svn/archives/PMO/team/shyan/temp "/>
<property name="local.dir" value="${basedir}"/>
<property name="classes" value="${basedir}\classes"/>
<property name="jar" value="hello.jar"/>
<property name="lib" value="${basedir}\lib"/>

<target name="init">
<tstamp>
<format property="today" pattern="yyyyMMdd" offset="0" unit="day"/>
</tstamp>
<property name="tag.curr.no" value="ITMaster_520_build_10006_${today}" />
<echo message="tag.curr.no:${tag.curr.no}" />
</target>

<target name="prepare" depends="init" description="Prepare the dirs for other tasks">
<delete dir="src"/>
<delete dir="classes"/>
<delete dir="lib"/>
<delete dir="log"/>
<mkdir dir="classes"/>
<mkdir dir="lib"/>
<mkdir dir="log"/>
</target>

<!--SVN CheckOut-->
<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask" />
<target name="checkout" depends="prepare">
<svn username="${svn.user}" password="${svn.password}" javahl="true">
<checkout url="${svn.url}" revision="HEAD" destPath="${local.dir}" />
</svn>
</target>

<!--Src Checkstyle-->
<target name="checkstyle" depends="checkout">
<ant antfile="checkstyle_build.xml" output="${basedir}\log\checkstyle_build-${today}.log" />
</target>

<!--run-->
<target name="compile" depends="checkstyle">
<javac srcdir="${basedir}\src" destdir="${classes}"/>
</target>


<target name="clean" depends="compile">
<jar jarfile="${lib}\${jar}" basedir="${classes}"/>
</target>

<!--Findbugs-->
<target name="findbugs" depends="clean">
<ant antfile="findbugs_build.xml" output="${basedir}\log\findbugs_build-${today}.log" />
</target>

<!--Ant Email-->
<target name="email">
<mail mailhost="192.168.1.212" mailport="25" subject="Test Ant">
<from address="yanshunhua@mantis.com"/>
<to address="yanshunhua@mantis.com"/>
<!--<cc address="limeng@mantis.com"/>-->
<message>This is test</message>
<fileset dir="${basedir}">
<include name="*.bak"/>
</fileset>
</mail>
</target>

<target name="start">
<ant target="findbugs"/>
<ant target="email"/>
</target>
</project>

关于build.xml 简单介绍:
<?xml version="1.0" encoding="UTF-8" ?> 这是xml的头文件
<project> 根节点
<project name="SVN Ant Test" default="start" basedir=".">
default是调用project根下面的某个target,name属性可以自己定义 ,basedir是根目录 注意到是basedir属性可以作为变量
<target>标签 target标签是把每一步功能划分开来

<target name="prepare" depends="init" description="Prepare the dirs for other tasks">
<delete dir="src"/>
<delete dir="classes"/>
<delete dir="lib"/>
<delete dir="log"/>
<mkdir dir="classes"/>
<mkdir dir="lib"/>
<mkdir dir="log"/>
</target>

<!--SVN CheckOut-->
<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask" />
<target name="checkout" depends="prepare">
<svn username="${svn.user}" password="${svn.password}" javahl="true">
<checkout url="${svn.url}" revision="HEAD" destPath="${local.dir}" />
</svn>
</target>

第一个target作用是删除老文件夹,创建新文件夹,后面一个target是自动从svn上迁出源代码.

我们把代码迁出来了,我们会发现ant自动生成了3个文件夹分别是src,classes,lib。src文件夹是放源码的,classes和lib是空的,作用后面讲。
这一步做好,我们需要面临一个选择:1。直接编译,2。java代码走查;一般正规的需要进行代码走查,于是延伸出了另一个xml文件;

<ant antfile="checkstyle_build.xml" output="${basedir}\log\checkstyle_build-${today}.log" />

这个脚本的作用是调用外部的*_build.xml文件并生成log日志。

ant标签还支持文件的增,删,移动
<delete dir="src"/>,这个就是删除名为src的文件夹. 注意到是每次构建环境需要一个干净的环境。

<!--run-->
<target name="compile" depends="checkstyle">
<javac srcdir="${basedir}\src" destdir="${classes}"/>
</target>

这个就是编译的target,简单不?一行命令(哈哈)。javac是jdk的命令,srcdir="${basedir}\src"这个属性就定义需要编译的远文件路径.destdir="${classes}"这个属性是把编译好的class文件放入指定的目录.

<target name="clean" depends="compile">
<jar jarfile="${lib}\${jar}" basedir="${classes}"/>
</target>

这个是制作jar包,jar就是class文件的集合,所有的class文件按照一定的目录结构打包成*.jar文件.
OK。这个搞定了,我们有面临一个选择
还是代码走查.
但这个代码走查就不是单单检测java文件了,这里的代码走查还需要对class文件与jar包进行走查.

<!--Findbugs-->
<target name="findbugs" depends="clean">
<ant antfile="findbugs_build.xml" output="${basedir}\log\findbugs_build-${today}.log" />
</target>

这个就是另一个代码走查工具,当编译好后,需要发送构建完成文件邮件
<target name="email">
<mail mailhost="192.168.1.212" mailport="25" subject="Test Ant">
<from address="yanshunhua@mantis.com"/>
<to address="yanshunhua@mantis.com"/>
<!--<cc address="limeng@mantis.com"/>-->
<message>This is test</message>
<fileset dir="${basedir}">
<include name="*.bak"/>
</fileset>
</mail>
</target>

这个就是通过ant发送编译结果邮件的target

<mail mailhost="192.168.1.212" mailport="25" subject="Test Ant"> mailport是SMTP邮箱端口 subject是邮件标题
<from address="yanshunhua@mantis.com"/> 发件人
<to address="yanshunhua@mantis.com"/> 收件人
<cc address="limeng@mantis.com"/> 抄送 (还有一个秘抄标签)
<fileset dir="${basedir}"><include name="*.bak"/></fileset> 这个是加载附件,dir="${basedir}"这个是本地路径,<include name="*.bak"/>所要发送的文件类型 *.*表示所有文件类型

<target name="start">
<ant target="findbugs"/>
<ant target="email"/>
</target>

这是最后的,也是最最主要的,这个标签和<project name="SVN Ant Test" default="start" basedir=".">这个标签中的default属性做对应,还有每个target表中中的depends属性,必须先运行完毕依赖的target才能运行自己的target这个属性作用是依赖另一个target标签.

这就是ant的核心,build.xml的最基本的格式和内容.
如果配置不懂的 里面有详细介绍 http://tieba.baidu.com/f?kz=794016836
pipeline { agent any environment { // 许可证配置 RLM_LICENSE = 'C:\\HighTec\\licenses\\license.lic' // Eclipse 无界面构建相关配置 ECLIPSE_EXE = 'C:\\HighTec\\ide\\htc-ide-v2.9.0\\eclipsec.exe' WORKSPACE_DATA = 'E:\\hightec-volant-test' PROJECT_PATH = '.\\M_MOT_VOLANT_STATE' // 构建输出目录 BUILD_OUTPUT_DIR = "${WORKSPACE}\\build-output" // 新增:两个打包文件夹路径(基于workspace) PACKAGE_FILES = "${WORKSPACE}\\package_files" // 存放.hex/.elf/.map和DBC文件 PACKAGE_PROJECT = "${WORKSPACE}\\package_project" // 存放工程文件和其他dev文件 // Eclipse参数配置 ECLIPSE_ARGS = '-nosplash -consolelog ' // GitLab配置 GITLAB_URL = 'http://git.santroll.com' GITLAB_PROJECT = 'volant/volant_tc375.git' GIT_CREDENTIALS_ID = '71e64a5e-1089-4cc9-8869-b1fb547f233f' // SVN配置 SVN_WORKSPACE = 'F:\\Document\\jenkins-test' SVN_REPO_URL = 'https://172.22.4.106/svn/Document/jenkins-test' SVN_CREDENTIALS_ID = 'SVN' SVN_LOG_FILE = "${WORKSPACE}\\svn_commit.log" // Git用户配置(临时) GIT_USER_NAME = 'Jenkins CI' GIT_USER_EMAIL = 'jenkins@example.com' } triggers { gitlab( triggerOnPush: false, triggerOnMergeRequest: true, branchFilterType: 'All', secretToken: "${env.GITLAB_WEBHOOK_TOKEN}" ) } parameters { string(name: 'SOURCE_BRANCH', defaultValue: 'Jenkins-test', description: '源分支名称(手动触发时使用)') string(name: 'TARGET_BRANCH', defaultValue: 'dev', description: '目标分支名称(手动触发时使用)') } stages { stage('环境准备') { steps { bat ''' @echo off chcp 65001 >nul echo 清理工作空间... if exist "%WORKSPACE%" rmdir /S /Q "%WORKSPACE%" if exist "%WORKSPACE_DATA%" rmdir /S /Q "%WORKSPACE_DATA%" echo 创建输出目录... mkdir "%BUILD_OUTPUT_DIR%" mkdir "%PACKAGE_FILES%" rem 创建第一个打包文件夹 mkdir "%PACKAGE_PROJECT%" rem 创建第二个打包文件夹 echo 验证Eclipse和许可证... if not exist "%ECLIPSE_EXE%" ( echo 错误: Eclipse可执行文件不存在 - %ECLIPSE_EXE% exit /b 1 ) if not exist "%RLM_LICENSE%" ( echo 错误: 许可证文件不存在 - %RLM_LICENSE% exit /b 1 ) echo 准备SVN工作目录... if not exist "%SVN_WORKSPACE%" ( mkdir "%SVN_WORKSPACE%" ) else ( echo 清理SVN工作目录旧文件... del /q "%SVN_WORKSPACE%\\*.*" ) ''' } } stage('检测分支信息') { steps { script { def sourceBranch = '' def targetBranch = params.TARGET_BRANCH echo "===== 触发方式调试信息 =====" echo "GITLAB_MERGE_REQUEST_IID: ${env.GITLAB_MERGE_REQUEST_IID}" echo "BUILD_CAUSE: ${env.BUILD_CAUSE}" echo "手动参数SOURCE_BRANCH: ${params.SOURCE_BRANCH}" echo "==========================" if (env.GITLAB_MERGE_REQUEST_IID) { echo "检测到GitLab MR触发: #${env.GITLAB_MERGE_REQUEST_IID}" sourceBranch = bat( script: "curl --header \"PRIVATE-TOKEN: ${GITLAB_API_TOKEN}\" " + "\"${GITLAB_URL}/api/v4/projects/${env.GITLAB_PROJECT_ID}/merge_requests/${env.GITLAB_MERGE_REQUEST_IID}\" | " + "jq -r '.source_branch'", returnStdout: true ).trim() targetBranch = bat( script: "curl --header \"PRIVATE-TOKEN: ${GITLAB_API_TOKEN}\" " + "\"${GITLAB_URL}/api/v4/projects/${env.GITLAB_PROJECT_ID}/merge_requests/${env.GITLAB_MERGE_REQUEST_IID}\" | " + "jq -r '.target_branch'", returnStdout: true ).trim() } else { echo "未检测到MR触发,使用手动参数分支" sourceBranch = params.SOURCE_BRANCH } if (!sourceBranch || sourceBranch == "null") { error "源分支为空,请检查配置或参数" } env.SOURCE_BRANCH = sourceBranch env.TARGET_BRANCH = targetBranch echo "确认合并信息:${sourceBranch} -> ${targetBranch}" } } } stage('代码拉取合并') { steps { script { bat "git config --global user.name \"${env.GIT_USER_NAME}\"" bat "git config --global user.email \"${env.GIT_USER_EMAIL}\"" checkout scm: [ $class: 'GitSCM', branches: [[name: "*/${env.TARGET_BRANCH}"]], userRemoteConfigs: [[ url: "${GITLAB_URL}/${GITLAB_PROJECT}", credentialsId: "${GIT_CREDENTIALS_ID}" ]] ] bat "git checkout ${env.TARGET_BRANCH}" bat "git pull origin ${env.TARGET_BRANCH}" def targetSHAOutput = bat(script: 'git rev-parse HEAD', returnStdout: true).trim() def targetSHA = (targetSHAOutput =~ /[0-9a-fA-F]{40}/)[0] ?: "" if (!targetSHA) { error "无法提取目标分支${env.TARGET_BRANCH}的SHA值,输出为:${targetSHAOutput}" } env.TARGET_SHA = targetSHA echo "目标分支${env.TARGET_BRANCH}最新SHA: ${targetSHA}" def sourceExists = bat( script: "git ls-remote --heads origin ${env.SOURCE_BRANCH} | find /c /v \"\"", returnStdout: true ).trim() if (sourceExists == "0") { error "源分支 ${env.SOURCE_BRANCH} 不存在" } bat "git checkout -b merge-test-branch" bat "git pull origin ${env.SOURCE_BRANCH}" def sourceSHAOutput = bat(script: 'git rev-parse HEAD', returnStdout: true).trim() def sourceSHA = (sourceSHAOutput =~ /[0-9a-fA-F]{40}/)[0] ?: "" env.SOURCE_SHA = sourceSHA echo "源分支${env.SOURCE_BRANCH}最新SHA: ${sourceSHA}" try { bat "git merge ${env.TARGET_SHA} -m \"模拟合并 ${env.SOURCE_BRANCH} 到 ${env.TARGET_BRANCH}\"" def mergeSHA = bat(script: 'git rev-parse HEAD', returnStdout: true).trim() mergeSHA = (mergeSHA =~ /[0-9a-fA-F]{40}/)[0] ?: "" env.MERGE_SHA = mergeSHA echo "合并成功,合并后SHA: ${mergeSHA}" } catch (Exception e) { echo "合并冲突或失败:${e.message}" bat "git diff --name-only --diff-filter=U" def isMerging = bat(script: 'if exist .git/MERGE_HEAD (echo 1) else (echo 0)', returnStdout: true).trim() if (isMerging == "1") { bat "git merge --abort" } error "合并失败,终止构建" } } } } stage('编译合并后代码') { steps { bat ''' @echo off echo 开始Eclipse无头构建... "%ECLIPSE_EXE%" %ECLIPSE_ARGS% ^ -application org.eclipse.cdt.managedbuilder.core.headlessbuild ^ -data "%WORKSPACE_DATA%" ^ -import "%PROJECT_PATH%" ^ -build all set ECLIPSE_EXIT_CODE=%ERRORLEVEL% if %ECLIPSE_EXIT_CODE% neq 0 ( echo [ERROR] Eclipse编译失败 exit /b 1 ) else ( echo [INFO] Eclipse编译成功 ) ''' bat ''' if exist "%PROJECT_PATH%\\iROM" ( robocopy "%PROJECT_PATH%\\iROM" "%BUILD_OUTPUT_DIR%" /E /NFL /NDL ) else ( echo [ERROR] 未找到编译输出目录iROM exit /b 1 ) exit /b 0 ''' } } stage('文件筛选打包') { steps { bat ''' @echo off setlocal enabledelayedexpansion :: 定义需要排除的关键目录 set "EXCLUDE_DIRS=%PROJECT_PATH% .git %BUILD_OUTPUT_DIR% %PACKAGE_FILES% %PACKAGE_PROJECT%" echo 1. 打包编译结果文件(.hex/.elf/.map)和DBC文件... robocopy "%BUILD_OUTPUT_DIR%" "%PACKAGE_FILES%" *.hex *.elf *.map /S /NFL /NDL if !ERRORLEVEL! geq 8 ( echo [ERROR] 复制编译结果文件失败 exit /b 1 ) robocopy "%WORKSPACE%" "%PACKAGE_FILES%" *.dbc /S /NFL /NDL /XD !EXCLUDE_DIRS! if !ERRORLEVEL! geq 8 ( echo [ERROR] 复制DBC文件失败 exit /b 1 ) echo 2. 打包合并后的工程文件和dev分支其他文件... robocopy "%PROJECT_PATH%" "%PACKAGE_PROJECT%\\工程文件" /E /NFL /NDL if !ERRORLEVEL! geq 8 ( echo [ERROR] 复制工程文件失败 exit /b 1 ) robocopy "%WORKSPACE%" "%PACKAGE_PROJECT%\\dev其他文件" /S /NFL /NDL /XD !EXCLUDE_DIRS! if !ERRORLEVEL! geq 8 ( echo [ERROR] 复制dev其他文件失败 exit /b 1 ) echo 两个打包文件夹已生成(无嵌套循环): echo - 文件包:%PACKAGE_FILES% echo - 工程包:%PACKAGE_PROJECT% ''' } } // 修复并完善:提取Git合并信息阶段(关键修改点) stage('提取Git合并信息') { steps { script { def mergeSHA = env.MERGE_SHA // 检查MERGE_SHA有效性 if (!mergeSHA || mergeSHA.length() != 40) { error "无效的MERGE_SHA: ${mergeSHA},无法提取Git信息" } // 关键修改:使用双引号包裹Git格式参数,兼容Windows批处理 def commitAuthor = bat( script: "git log -1 --format=\"%%an\" ${mergeSHA}", // %%an在Groovy中会解析为%an returnStdout: true ).trim() def commitEmail = bat( script: "git log -1 --format=\"%%ae\" ${mergeSHA}", returnStdout: true ).trim() def commitDate = bat( script: "git log -1 --format=\"%%ad\" --date=iso ${mergeSHA}", returnStdout: true ).trim() def commitMessage = bat( script: "git log -1 --format=\"%%s\" ${mergeSHA}", returnStdout: true ).trim() // 保存到环境变量(处理空值) env.GIT_COMMIT_AUTHOR = commitAuthor ?: "未知作者" env.GIT_COMMIT_EMAIL = commitEmail ?: "unknown@example.com" env.GIT_COMMIT_DATE = commitDate ?: new Date().format('yyyy-MM-dd HH:mm:ss') env.GIT_COMMIT_MESSAGE = commitMessage ?: "无提交信息" echo "提取的Git合并信息:" echo "合并SHA: ${mergeSHA}" echo "作者: ${env.GIT_COMMIT_AUTHOR} <${env.GIT_COMMIT_EMAIL}>" echo "提交日期: ${env.GIT_COMMIT_DATE}" echo "提交信息: ${env.GIT_COMMIT_MESSAGE}" } } } stage('上传到SVN') { steps { script { // 复制两个打包文件夹到SVN工作目录 bat ''' @echo off echo 复制文件包到SVN工作目录... robocopy "%PACKAGE_FILES%" "%SVN_WORKSPACE%\\文件包" /E /NFL /NDL /XO if %ERRORLEVEL% leq 7 exit /b 0 else exit /b %ERRORLEVEL% echo 复制工程包到SVN工作目录... robocopy "%PACKAGE_PROJECT%" "%SVN_WORKSPACE%\\工程包" /E /NFL /NDL /XO if %ERRORLEVEL% leq 7 exit /b 0 else exit /b %ERRORLEVEL% ''' // SVN提交(使用Git信息生成日志) withCredentials([usernamePassword(credentialsId: env.SVN_CREDENTIALS_ID, passwordVariable: 'SVN_PWD', usernameVariable: 'SVN_USER')]) { bat """ @echo off cd /d "%SVN_WORKSPACE%" echo 执行SVN更新... svn update --username %SVN_USER% --password %SVN_PWD% ^ --trust-server-cert-failures=unknown-ca,cn-mismatch ^ --non-interactive if %ERRORLEVEL% neq 0 ( echo SVN更新成功 ) else ( echo [ERROR] SVN更新失败 exit /b 1 ) echo 生成SVN提交日志(基于Git合并信息)... echo "Git合并提交信息:" > "%SVN_LOG_FILE%" echo "合并SHA: ${env.MERGE_SHA}" >> "%SVN_LOG_FILE%" echo "作者: ${env.GIT_COMMIT_AUTHOR} <${env.GIT_COMMIT_EMAIL}>" >> "%SVN_LOG_FILE%" echo "提交日期: ${env.GIT_COMMIT_DATE}" >> "%SVN_LOG_FILE%" echo "提交信息: ${env.GIT_COMMIT_MESSAGE}" >> "%SVN_LOG_FILE%" echo "源分支: ${env.SOURCE_BRANCH}" >> "%SVN_LOG_FILE%" echo "目标分支: ${env.TARGET_BRANCH}" >> "%SVN_LOG_FILE%" echo 提交到SVN... svn add --force * --depth infinity svn commit --username %SVN_USER% --password %SVN_PWD% -F "%SVN_LOG_FILE%" ^ --encoding utf-8 ^ --trust-server-cert-failures=unknown-ca,cn-mismatch ^ --non-interactive if %ERRORLEVEL% equ 0 ( echo SVN提交成功 ) else ( echo [ERROR] SVN提交失败 exit /b 1 ) """ } } } } } post { always { echo '构建流程结束' } success { script { if (env.GITLAB_MERGE_REQUEST_IID) { echo "[INFO] MR #${env.GITLAB_MERGE_REQUEST_IID} 构建成功,两个打包文件夹已上传SVN" } else { echo "[INFO] 手动构建成功: ${env.SOURCE_BRANCH} -> ${env.TARGET_BRANCH},两个打包文件夹已上传SVN" } } } failure { script { if (env.GITLAB_MERGE_REQUEST_IID) { echo "[ERROR] MR #${env.GITLAB_MERGE_REQUEST_IID} 构建失败" } else { echo "[ERROR] 手动构建失败: ${env.SOURCE_BRANCH} -> ${env.TARGET_BRANCH}" } } } } }请帮我分析 这段代码
最新发布
08-01
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值