UML软件工程组织

 

 

NAnt自动编译之废话谈
 
2008-08-04 来源:网络
 

我越发觉得我有写中文Blog的需要了,尤其是技术文章。

此前我阐述了我为什么写英文Blog的原因,还是有很多朋友问我。然而我相信我会坚持的,毕竟无论从数量还是质量上来看,我并不是一个合格的Blogger。很多原因导致了不合格,也许我总是脱离了Blog的初衷,不该老是在面对观众,另外关于思考的时间,我还是要每天坚持更多。关于英文的感受还是颇深:用中文四五句话可以说完的一篇文章,用英文可以拖拉一大串,久而久之,不仅弥补了生怕没有把网页撑的满满的歉疚,还能忽悠一下那些没有信心一口气读完满篇挫词滥语的观众们-喔,sorry,这不是我的本意,请不要怀疑我对你们的真诚。

在我的intraVnews的收藏夹里面,除了朋友同学的几个Feeds是中文外,其它的绝大多数都是英文的。不敢否认,我的大部分兴趣全部投入到了技术上面,关于技术,而且是那些更新不断的技术,勿庸置疑地让我孤注一掷在更多的国外的文章,专家,作者的拉丁字母之下。除此,工作的环境,剥夺了我一日中绝大部分时间使用中文的机会,于是乎把Blog迁就成英文也是水到渠成。

在过去的一年多时间里,我发现我的朋友圈虽然在渐渐变大,但是了解我的人,远比我要了解的人少之又少。在订阅朋友的Blog和坚持每日必读后,终于发现原来有很重要的一点是我脱离了群众路线,Blog上很久没有报料隐私,有也不是中文,于是乎霎有心得,便有此篇和此刻的心情。

上周我忙了一件事,做自动编译的脚本。

客户安装程序+每日构建+自动编译这个工作已经拖了很久了。趁Build服务器和网络重整间隙,把工作完成,终于松了口气。现在总结一下,作为一个中小型企业软件每日构建与发布的一个案例,有用处处大家讨论一下,有问题的欢迎斗批。再罗嗦一下,其实这些都是CM工作的范畴,只是当时我误入安装程序开发太深,加上资源没有到位,所以这份苦差才落到我的头上。关于Daily Build和*ant这些东西,其实是很简单的,就像曾面试过我的一个MVP反问我所言:“何不用批处理了事?”

目标:编写自动化脚本,自动完成从仓库提取源文件,编译,拷贝,打包,发布,邮件提醒等工作。

技术工具:NAnt, NAntContrib等。

场景:Windows 2003 VMware Image, .Net SDK 2.0, Surround SCM等。

由于涉及一些商业上的不得已,详细的需求就免了,虽然我这个人喜欢做事之前,把条条框框做的有模有样的。

在我最初的Nant脚本中完成的都是拷贝移动目录之类的活,是没有获取源代码这块的。熟悉Ant或Nant的应该清楚,Nant本身只提供了从VSS或者CVS仓库访问源代码的接口。而我们用的是Surround SCM,虽然也是多届Jolt大奖的得主,但我坚决肯定对Nant是不会提供直接访问支持。终于在Seapine网站Surround SCM的资源里面查到有Ant的支持,但是没有Nant。没有关系,既然Nant从Ant得自真传,Java与C#也成不了问题。提供Ant的接口虽然是免费,但是Surround SCM的商业软件,他们也不会笨的把接口开源,于是从朋友那里要了号称很牛的Java反编译工具JShrink,开始读Class反编译后的Java代码。

Outlook不自觉地来了个提醒,原来我只安排一天工作时间解决脚本和发布,如果继续,虽然我只要依葫芦画瓢,加上测试至少得一天时间,而且可靠性还得打折,毕竟不是一个主,还不是一个太监服侍的。

随手Google了一把 nant + SCM,天上居然还真掉下个馅饼,有个NAntContrib项目,早就帮我完成了对SCM访问的Task。这个教训告诉我们,做任何自认为有创意的挑战性任务前,别忘记Google一下是否有前车之鉴。

拿到源代码就是开始编译了。我下载的NAnt是0.85的Stable版本。在我修改脚本想使用.net 2.0进行源代码编译时候,居然告诉我不支持当前.net的SDK版本。真是Faint了,还以为NAnt对.Net SDK和Runtime的Configuration有多强,一上来就吃了一个闭门羹。吸取前一次教训,Google一把,果真还有人告诉我怎么把NAnt.exe.config中配置成2.0版本的。这里发现这个版本发布时候,提供支持的最高.net framework版本是2.0Beta,于是我把它改成Release的版本,重新编译,结果在用调用NGen编译资源文件时候又报了一个错。难道这个NAnt不支持.net 2.0 Release版本?如果是这样,我就疯掉了。打开NAnt的首页,几行红字清楚告诉有个最新的Night Build版本已经支持.net 2.0 release 版本,Cool!这个教训告诉我们,Google得到的也不一定是最好的答案,遇到问题你碰壁的时候,不妨再回到问题的本身重头再找一下线索。

拿到最新版本重新编译,果然没有问题。现在取源代码,编译,拷贝都完成了,怎么发布到下载服务器呢?拷贝到远程服务器,第一个让我想到的是SCP,我曾经在Linux环境下做过一段时间的开发,对这个SSH下的命令工具特有感情,所以第一时间想到了它。虽然在NAntContrib包里也找到了相关的Task,但是最后多次测试之后还是放弃了。我们的发布服务器提供了FTP访问,难道NAnt没有提供这个最基本的Task,奇怪。 再次Google,终于找到一个FTPTask!再次证明了信息社会,获取资源信息的速度远比你本身掌握的信息量更加实用。

到这里一个自动的NAnt脚本终于可以拿出来Show一把了,

<?xml version="1.0"?>
<project name="Product 2.4" default="Success">
 <description>Product 2.4 Release Build file for NAnt. By
Chagel@gmail.com </description>
 <property name="verbose" value="true"/>
 <property name="nant.settings.currentframework" value="net-2.0" />
 <property name="file.package" value="Product 2.4.16.1.rar" />
 <property name="file.build.setup" value="Product24_Setup.Build" />
 <property name="file.build.web" value="Product24_Web.Build" />
 <property name="file.build.rollingforecast" value="Product24_RollingForecast.Build" />
 <property name="publish.root" value="C:\Work\Nightly Build" />
 <property name="ftp.remotehost" value="147.183.216.42" />
 <property name="ftp.username" value="username" />
 <property name="ftp.password" value="password" />
 <property name="ftp.remotedir" value="/Project/Installation Build" />
 <property name="build.root" value="C:\temp\Product 2.4" />
 <property name="repository.server" value="147.183.216.11:4900" />
 <property name="repository.authen" value="user:password" />
 <property name="repository.branch" value="Product" />
 <property name="folder.Common" value="Common/ESDlls" />
 <property name="folder.Documents" value="Product 2.4 Documents" />
 <property name="folder.Setup" value="Setup" />
 <property name="folder.SharedBooks" value="SharedBooks" />
 <property name="folder.Data" value="Data" />
 <property name="folder.Web" value="Web" />
 <property name="folder.build" value="Build" />
 <property name="folder.build.setup" value="Build/Setup" />
 <property name="folder.build.web" value="Build/Web" />
 <property name="folder.build.rollingforecast" value="Build/RollingForecast" />

 <!--// Create the root for build files -->
 <target name="Create">
  <delete dir="${build.root}" unless="${directory::exists('build.root')} "/>
  <mkdir dir="${build.root}" />
  <mkdir dir="${build.root}/${folder.Common}" />
  <mkdir dir="${build.root}/${folder.Documents}" />
  <mkdir dir="${build.root}/${folder.SharedBooks}" />
  <mkdir dir="${build.root}/${folder.Setup}" />
  <mkdir dir="${build.root}/${folder.Web}" />
 </target>
 
 <!--// Get source files from reposity of SCM -->
 <target name="Get">
  <!--// Get DLL files -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/CM/DLLS"
      writable="true"
      destdir="${build.root}/${folder.Common}"
  />
  
  <!--// Get Web files -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/SRC/Web"
      writable="true"
      recursive = "true"
      destdir="${build.root}/${folder.Web}"
  />
  
  <!--// Get ESFacade -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="ESFacade.dll"
      branch="${repository.branch}"
      repository="Product/SRC/Projects/Product Facade"
      writable="true"
      destdir="${build.root}/${folder.Web}/BIN"
  />
  
  <!--// Get Data files -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/CM/Setup/Data"
      writable="true"
      recursive = "true"
      destdir="${build.root}/${folder.Data}"
  />
  
  <!--// Get Setup source -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/CM/Setup/Product Setup/Build"
      writable="true"
      recursive = "true"
      destdir="${build.root}/${folder.build.setup}"
  />
  
  <!--// Get web source -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/SRC/Web/App_Code"
      writable="true"
      recursive = "true"
      destdir="${build.root}/${folder.build.web}"
  />
  
  <!--// We need some reference assemblies to compile web project -->
  <copy todir="${build.root}/${folder.build.web}" verbose="true">
      <fileset basedir="${build.root}/${folder.Web}/BIN">
          <include name="adodb.dll" />
          <include name="Coalesys.WebMenu.dll" />
          <include name="Interop.ADOMD.dll" />
          <include name="Interop.CIPLICENSELib.dll" />
      </fileset>
  </copy>

  
  <!--// Get RollingForecast source -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/SRC/Projects/ROLLLINGFORECAST"
      writable="true"
      recursive = "true"
      destdir="${build.root}/${folder.build.rollingforecast}"
  />
  
  <copy todir="${build.root}/${folder.build.rollingforecast}/Res" verbose="true">
      <fileset basedir="${build.root}/${folder.Web}/BIN">
          <include name="ESFacade.dll" />
      </fileset>
  </copy>
  
  <!--// Get Release notes and license file -->
  <sscmget
      serverconnect="${repository.server}"
      serverlogin="${repository.authen}"
      file="/"
      branch="${repository.branch}"
      repository="Product/CM/Setup/Release Notes"
      writable="true"
      recursive = "true"
      destdir="${build.root}"
  />
 </target>
 
 <!--// Compile the sources -->
 <target name="Compile">
  <mkdir dir="${build.root}\${folder.build.web}\Build" />
  <mkdir dir="${build.root}\${folder.build.rollingforecast}\Build" />
  <csc target="library" output="${build.root}\${folder.build.web}\Build\ES24.dll" rebuild="true" define="NET,NET_2_0">
   <nowarn>
     <warning number="0168" />
     <warning number="0219" />
   </nowarn>
      <sources>
          <include name="${build.root}\${folder.build.web}\*.cs" />
      </sources>
      <references basedir="${build.root}/${folder.build.web}">
          <include name="System.dll" />
          <include name="System.Data.dll" />
          <include name="System.Web.dll" />
          <include name="adodb.dll" />
          <include name="Coalesys.WebMenu.dll" />
          <include name="Interop.ADOMD.dll" />
          <include name="Interop.CIPLICENSELib.dll" />
      </references>
  </csc>


  <csc target="library" output="${build.root}\${folder.build.rollingforecast}\Build\RolllingForecast.dll" rebuild="true" define="NET,NET_2_0">
   <nowarn>
     <warning number="0168" />
     <warning number="0219" />
     <warning number="0618" />
   </nowarn>
      <sources>
          <include name="${build.root}\${folder.build.rollingforecast}\**\*.cs" />
      </sources>
      <resources>
          <include name="${build.root}\${folder.build.rollingforecast}\**\*.resx" />
      </resources>

      <references basedir="${build.root}/${folder.build.rollingforecast}/Res">
          <include name="System.dll" />
          <include name="System.Data.dll" />
          <include name="System.Web.dll" />
          <include name="System.Collections.dll" />
          <include name="System.Collections.Generic.dll" />
          <include name="ESFacade.dll" />
          <include name="Microsoft.AnalysisServices.DLL" />
      </references>
  </csc>

 </target>

 <!--// Copy Compiled files to build folder -->
 <target name="Copy">
  <!--// Setup files -->
  <copy todir="${build.root}" verbose="true">
      <fileset basedir="${build.root}/${folder.build.setup}">
       <include name="*.*" />
          <exclude name="Product 2.4.vshost.exe.config" />
          <exclude name="Product 2.4.vshost.exe" />
      </fileset>
  </copy>
  
  <!--// Setup files for uninstall-->
  <copy todir="${build.root}/${folder.Setup}" verbose="true">
      <fileset basedir="${build.root}/${folder.build.setup}">
       <include name="*.*" />
          <exclude name="Product 2.4.vshost.exe.config" />
          <exclude name="Product 2.4.vshost.exe" />
      </fileset>
  </copy>
  
  <!--// license files for uninstall -->
  <copy todir="${build.root}/${folder.Setup}" verbose="true">
      <fileset basedir="${build.root}">
       <include name="*.rtf" />
      </fileset>
  </copy>
  
  <!--// Web Assembly -->
  <copy todir="${build.root}/${folder.Web}/BIN" verbose="true">
      <fileset basedir="${build.root}/${folder.build.web}/Build">
          <include name="*.dll" />
      </fileset>
  </copy>
  
  <!--// RollingForecast Assembly -->
  <copy todir="${build.root}/${folder.Web}/BIN" verbose="true">
      <fileset basedir="${build.root}/${folder.build.rollingforecast}/Build">
          <include name="*.dll" />
      </fileset>
  </copy>
 </target>
 
 <!--// Clean the temporary source files -->
 <target name="Clean">
  <delete dir="${build.root}/${folder.build}" failonerror="true" />
  <delete dir="${build.root}/${folder.Web}/App_Code" failonerror="true" />
  <delete>
      <fileset>
          <include name="${build.root}/${folder.Web}/**/*.cs" />
      </fileset>
  </delete>

 </target>
 
 <!--// Pack all files to RAR package -->
 <target name="Pack">
  <zip zipfile="${build.root}/${file.package}" includeemptydirs="true">
   <fileset basedir="${build.root}">
     <include name="**/*" />
   </fileset>
  </zip>
 </target>
 
 <!--// Upload build files to FTP site -->
 <target name="Upload">
  <connection id="FTPConn" server="${ftp.remotehost}" username="${ftp.username}" password="${ftp.password}" />
  <ftp connection="FTPConn">
         <put type="bin" localdir="${build.root}" remotedir="${ftp.remotedir}" flatten="true">
            <include name="${file.package}"/>
        </put>
  </ftp>
 </target>
 
 <!--// Publish the Setup package -->
 <target name="Publish">
  <move todir="${publish.root}" overwrite="true">
      <fileset basedir="${build.root}">
          <include name="${file.package}" />
      </fileset>
  </move>
 </target>

   
 <target name="Success" depends="Create, Get, Copy, Compile, Clean, Pack, Upload, Publish">
  <mail
      from="MikeChen@***.com"
      tolist="ChristieDuan@***.com;JennyZhou@***.com;NicoleFang@***.com;ElvisTang@***.com;JoeXu@***.com;MikeChen@***.com"
      cclist="DavidGeng@***.com;CindyCai@***.com;KarstenJakobsen@***.com"
      subject="${file.package} is available to be downloaded"
      mailhost="mail.***.com"
      format="Text"
      message="[Message from BUILDSERVER] Download the latest build of Product 2.4(Codename: Shark) from:
FTP://${ftp.remotehost}${ftp.remotedir}/${file.package}">
      <files>
          <include name="${build.root}/Release Note.txt" />
      </files>
   <attachments>
          <include name="${directory::get-current-directory()}/*.log" />
      </attachments>

  </mail>
 </target>
</project>

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号