從www.eclipse.org上下載Eclipse原始碼包,解壓縮。發現Eclipse的原始碼目錄和和最終編譯出來的目錄結果基本一摸一樣,在根目錄下,也是一個plugins子目錄和一個features子目錄,唯一缺少的是那個startup.jar和eclipse.exe。
到www.eclipse.org上看了一下build instruction,發現編譯非常簡單,同樣使用的是ant(幸好前兩天搞定了ant,^_^)。有一個build.bat指令碼,要求按照不同的平台傳遞幾個參數,分別是-os 作業系統,-ws 視窗系統,-arch CPU構架,然後build指令碼根據這幾個參數選擇不同的build.xml檔案,再調用ant來做編譯工作。具體的參數可以查看Eclipse的編譯協助文檔,我使用的是windows xp,所以編譯參數是build -os win32 -ws win32 -arch x86。
需要注意的是,開始編譯前要設定好ANT_HOME路徑,簡單的做法是,開始菜單-->運行,敲入set ANT_PATH=d:/apache-ant-1.6.0。我建議最好把ant的bin目錄設定到PATH環境變數下,因為Eclipse的每個外掛程式自身都帶有build.xml檔案,其中大部分組件都是可以直接在其目錄下敲入ant直接編譯的,如果你修改了某個組件的部分代碼,完全可以簡單的敲入ant來觀察結果,而不需要回到根目錄做完全的編譯。
應該說的是,Eclipse本身的編譯過程不算太慢。我的P4 2.8/1G RAM機器上,也就15分鐘。但是如果當我改變了某個外掛程式的某些檔案,然後再回到根目錄做部分編譯時間,我發現居然跟完整編譯一次花費的時間差不多,因為很多沒有改變的外掛程式也都被重新編譯打包了,這讓我覺得很是不爽,不知道為什麼會有這種設計,看來需要分析一下Eclipse的build指令碼。
完成編譯後,會發現在根目錄下出現一個tmp子目錄,下面是3個目錄,plugins,features,以及一個隨平台不同而變的目錄,在我的系統下叫做setup.x86。plugins下放著所有編譯出來的外掛程式,features下則存放這所有的feature。那個win32.win32.x86下,正好就是運行Eclipse所需要的eclipse.exe和startup.exe檔案,我試著把這兩個檔案拷貝到tmp目錄下,然後雙擊eclipse.exe,那個長得很困難的Eclipse Splash居然就出現了,然後整個程式啟動,一切OK。
為了對整個編譯過程有一個理性的認識,我跟蹤了一下整個Eclipse的編譯過程,其實就是觀察多個builk.xml之間的調用關係。
1. 根目錄的build.xml檔案有一個target叫做allElements,其指令碼如下:
<target name="allElements">
<ant antfile="${buildDirectory}/features/org.eclipse.sdk/build.xml" target="${target}" dir="${buildDirectory}/features/org.eclipse.sdk" />
</target>
顯然,首先被編譯的是features目錄下的org.eclipse.sdk下的build.xml檔案,而且執行的目標target是build.jar(瞭解這個屬性需要閱讀eclipse_src/build.xml)。所有這幾行指令碼的意義就是:在features/org.eclipse.sdk目錄下執行ant build.jar
2. 然後分析org.eclipse.sdk/build.xml檔案中build.jar對應的指令碼,
<target name="build.jars" depends="init" description="Build all the jars for the feature: org.eclipse.sdk.">
<antcall target="all.children">
<param name="target" value="build.jars"/>
</antcall>
</target>
沿著build.jar的depend,一直跟蹤到all.features這個目標,一切都明朗了
<target name="all.features" depends="init">
<ant antfile="build.xml" dir="../org.eclipse.platform/" target="${target}"/>
<ant antfile="build.xml" dir="../org.eclipse.platform.source/" target="${target}"/>
<ant antfile="build.xml" dir="../org.eclipse.jdt/" target="${target}"/>
<ant antfile="build.xml" dir="../org.eclipse.jdt.source/" target="${target}"/>
<ant antfile="build.xml" dir="../org.eclipse.pde/" target="${target}"/>
<ant antfile="build.xml" dir="../org.eclipse.pde.source/" target="${target}"/>
</target>
顯然,這個target的意思就是編譯features下的另外6個feature的build.xml檔案。而且,這裡其實可以看到Eclipse的外掛程式的層次關係,SDK調用了Platform,JDT,PDE各自的build.xml,顯然就是說這3個外掛程式構成了SDK。
3. 剩下的跟蹤步驟與前面相同,Platform調用所有構成Platform的外掛程式的build.xml,JDT調用構成JDT的外掛程式的build.xml,看一個例子,在features/org.eclipse.platform/build.xml下的一行:
<target name="all.plugins" depends="init">
<ant antfile="build.xml" dir="../../plugins/org.eclipse.osgi" target="${target}">
<property name="arch" value="x86"/>
<property name="os" value="win32"/>
<property name="ws" value="win32"/>
</ant>
...其它外掛程式的調用
顯然,每個在plugins目錄下的外掛程式都是通過這種方式編譯的。
4. 看一個具體的外掛程式的build.xml檔案,org.eclipse.core.runtime/build.xml中的一段,描敘了整個編譯的過程,當然,打包部分在另一個target 中:
<target name="runtime.jar" depends="init" unless="runtime.jar" description="Create jar: runtime.jar.">
<delete dir="${temp.folder}/runtime.jar.bin"/> //刪除臨時目錄
<mkdir dir="${temp.folder}/runtime.jar.bin"/> //建立臨時目錄
<!-- compile the source code -->
//編譯所有的.java檔案,源碼目錄在隨後的<src path=“src“/>指定。
<javac destdir="${temp.folder}/runtime.jar.bin" failonerror="${javacFailOnError}" verbose="${javacVerbose}" debug="${javacDebugInfo}" includeAntRuntime="no" bootclasspath="${bootclasspath}" source="${javacSource}" target="${javacTarget}" >
<compilerarg line="${compilerArg}"/>
<classpath>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/core.jar"/>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/console.jar"/>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/osgi.jar"/>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/resolver.jar"/>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/defaultAdaptor.jar"/>
<pathelement path="${build.result.folder}/../org.eclipse.osgi/eclipseAdaptor.jar"/>
</classpath>
<src path="src/" />
</javac>
<!-- Copy necessary resources -->
//複製一些非代碼的資源檔,比片
<copy todir="${temp.folder}/runtime.jar.bin" failonerror="true">
<fileset dir="src/" excludes="**/*.java, **/package.htm*" />
</copy>
//打包
<mkdir dir="${build.result.folder}"/>
<jar jarfile="${build.result.folder}/runtime.jar" basedir="${temp.folder}/runtime.jar.bin"/>
//刪除臨時目錄
<delete dir="${temp.folder}/runtime.jar.bin"/>
</target>
對Eclipse的分析終於完成了,也解除了我先前的疑惑。當在Eclipse源碼根目錄敲入build命令時,編譯進入eclipse_src/build.xml的“compile”這個target,它老兄第一件事就是先執行“init“和“clean“這兩個target,然後再調用allElements,以下這句指令碼就是了:
<target name="compile" depends="init,clean">
所以,可以說,Eclipse是沒有部分編譯這個概念的,唯一可想的辦法就是自己進入想要重新編譯的plugin,執行:
ant clean
ant build.jar
game over!