編者注:在這一期的 開源之路 中,作者和 Cafe Au Lait 的創始人 Elliotte Rusty Harold 接管了目前的 Java SE 7 開發系列文章。因為 OpenJDK 項目 的目標和流程已經在 第一期 介紹過,所以 Elliotte 將在此教程中直接介紹 JDK 7 的實際構建。
在此之前, 如果 OpenJDK 項目有更新,我們會將其發布在每期“開源之路”的開始部分。最新的 發行版 是 b23,發行日期是 10 月 30 日。此發行版解決了一小部分缺陷和功能要求,這些內容可詳見其 發行說明,其中許多都與管理問題相關,比如頂層 README 文件、空格清理和遺留 "j2se" 引用的移除。上一個版本 b22 解決的問題比較多,集成了 65 個缺陷修復和 14 個功能,其中包括 將 CORBA、JAXP 和 JAXWS 分割到各自的工作區中,將 Swing ThreadPool 創建 替換為 java.util.concurrent 功能,支持各種時間區更改,以及 OpenJDK RenderingEngine 插件的創建,這意味著“為 Open JDK 提供了替代 Ductus 庫的起點。”
但如何達到所有這些新目標呢?這正是 Elliotte 將在在本文中闡述的內容,他將介紹如何從源碼構建 JDK。
由於 Sun 的 Java 開發工具包是自由軟件(只需在替換的過程中對一些小組件建模),因此我們不妨對經進行一些修改。無論您的激情在於優化、實驗、語言設計、調試還是文檔,都有大量的工作需要您來做,都有大量的機會提供給各種技能水平的開發人員。在本系列的後續文章中,我將詳述 Java 7 或更高版本中可能出現的各種 API 開發。但是,要玩轉這些 API,您將需要浴血奮戰,所以立即甩掉您的急救箱,撕掉止血帶,准備流一些血吧!我們要開始構建 JDK 了。
系統需求
首先,構建 JDK 需要一個受支持的操作系統。這包括 Linux、Solaris、Windows XP 和 Windows Vista。不支持 Mac OS X。Apple 負責將 JDK 遷移至 Mac 中,然而,這往往滯後於潮流。但是,Mac 用戶可以使用 Parallels、VMWare Fusion 或 Boot Camp 來運行 Windows 或 Linux,並在其中構建 JDK。的確,我撰寫此文時用的就是這種方法,當時我電腦中的以太網卡在項目完成前兩天意外死亡了。然而,在 Apple 發布最新的 JDK 之前,我們仍然無法在 Mac OS 中運行 Java 7(或 6)應用程序。這可能發生在明天,可能發生在明年,也可能永遠都不會發生。
其次,我們需要一個最新的 Java 6 SDK。Java 7 不能通過 Java 5 來編譯。就我個人來說,我在設計軟件時不喜歡太多的版本依賴,這對於開源軟件尤其重要。但是,JDK 只是在最近才開放源碼,而且顯示了公司 IT 的一些遺留態度:“我們可以控制每個人的構建環境”。移除其中的一些依賴是一項正在進行的並將花費數年的任務,但是,這個過程已經取得了初步進展。就在最近,Sun 將其源碼庫從專用 Teamware 轉移到了開源的 Mercurial 中。從封閉開發模式切換到開放開發模式是一項艱巨的任務,但最終結果是更強大、更靈活、更健壯的代碼庫。
第三,我們需要一個 C 編譯器。其中一些 JDK 是用原生代碼編寫的。它們不可能都是純 Java 編寫的。對於 Linux,我們需要使用 gcc4。結於 Windows,需要使用 Microsoft Visual Studio .NET。對於 Solaris,需要使用 Sun Studio 11。
在 Linux 中,可能還需要安裝或更新一些庫。具體要安裝哪些庫,取決於您的發行版和版本。您可能還需要為現有庫安裝一組 C 頭文件。在本文中,我使用現有的 Ubuntu 7.10 Gutsy Gibbon 發行版。大多數其他合理流通的發行版應該也能工作。如果您發現有一些發行版無法工作,不妨找出其原因並記錄缺陷。
最後,在 Windows 中,您可以在 NTFS 文件系統上進行構建。您不能在 FAT-32 上構建 JDK。您還需要安裝 Cygwin,因為在 Windows 上構建的 JDK 是 Windows 和 Unix 實用工具的奇異混合物。
獲取源碼
大約每隔一個月,Sun 會在 OpenJDK 源碼發布頁面 上發布一組完整的 JDK 源碼。下面列出了幾種不同的包:
OpenJDK 源碼 JDK 7 大約 95% 的主要源碼。 平台的二進制插件 Sun 實際並不擁有 JDK 中的所有代碼,並且他們無法重新注冊他們不擁有的代碼。相反,一些代碼段必須以閉源二進制軟件包來提供。您將需要為您的平台下載其中一些代碼。Linux、Solaris 和 Windows 在 32 位和 64 位版本中都受支持 。 Jtreg 測試工具二進制軟件包下載 代碼的測試框架。您不必真正使用測試框架來構建或修改代碼,但是不論如何您都應該掌握它。 OpenJDK 模塊項目 這包括 Java 7 的新模塊系統。(我將在本系列的後續文章中介紹。) 最終,此項目將匯總到 JDK 中,但現在您並不立即需要它。
因為這些項目所占空間在 120MB 以上,下載服務器的速度可能不是一直都特別快,所以要想全部下載可能需要一段時間。開源的一個好處就是不用單擊通過令人討厭的使用許可。從普通的 URL 中可以下載任何開源軟件。這使得 curl、wget 等類似工具的使用更加容易。設置批處理作業來獲取這些工具,然後您就可以悠閒地喝一杯咖啡。事實上,無點擊還使正常浏覽器的使用更加容易。點擊通過注冊的方式應該可以廢除了。點擊通過注冊只是使律師更加忙碌,除了律師誰還需要這些東西?
jars@jars-desktop:~/openjdk$ wget
http://www.java.net/download/openjdk/jdk7/promoted/b23/openjdk-7-ea-src-b23-30_oct_2007.zip
--18:02:02--
http://www.java.net/download/openjdk/jdk7/promoted/b23/openjdk-7-ea-src-b23-30_oct_2007.zip
=> `openjdk-7-ea-src-b23-30_oct_2007.zip'
Resolving www.java.net... 64.125.132.37, 64.125.132.39
Connecting to www.java.net|64.125.132.37|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location:
http://download.java.net/openjdk/jdk7/promoted/b23/openjdk-7-ea-src-b23-30_oct_2007.zip
[following]
--18:02:02--
http://download.java.net/openjdk/jdk7/promoted/b23/openjdk-7-ea-src-b23-30_oct_2007.zip
=> `openjdk-7-ea-src-b23-30_oct_2007.zip'
Resolving download.java.net... 72.5.124.114
Connecting to download.java.net|72.5.124.114|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 84,617,174 (81M) [application/zip]
44% [===============> ] 37,717,212 55.36K/s ETA 12:19
現在,我們只需要前兩個條件:OpenJDK 源碼和二進制插件。但是,將來您可能還需要其他兩個條件。
Mercurial
Sun 尚未完全開放 JDK 源碼控制庫。然而,這隨時都可能發生。他們正在使用的系統是 Sun 專用產品,名為 Teamware。他們正在轉換到開源的 Mercurial(不是子版本或 CVS)。轉換完成之後,您就能夠使用絕對最新的商業版本。
忽略 OpenJDK 站點上對子版本的所有引用。這些僅是 java.net 項目模板的標准部分。Sun 實際並未使用 java.net 庫或子版本來管理 OpenJDK。子版本中實際上只有 openjdk.java.net 網站的 HTML 代碼。真正的 JDK 源碼並不在子版本中。
下載快照版本會為您提供可能不會構建的代碼(雖然有一些快照已經完全破壞)。然而,代碼也可能過時一個多月或更久了。檢查 Mercurial 中的代碼會得到絕對最新的代碼。然而,這些代碼可能完全破壞了,您無法確定如果構建失敗到底是您的錯誤還是代碼的錯誤。完成庫的開發之後,我建議您首先從已知的好快照開始構建,來解開整個過程的所有疙瘩,然後在您確信自己的設置之後,前進到版本控制中最新的部分。
編譯
現在,下載以下軟件包,並解壓它們:
$ unzip openjdk-7-ea-src-b23-30_oct_2007.zip
inflating: openjdk/control/make/Makefile
inflating: openjdk/control/make/README
inflating: openjdk/control/make/jprt.config
inflating: openjdk/control/make/jprt.properties
inflating: openjdk/control/make/make/Defs-internal.gmk
inflating: openjdk/control/make/make/README.pre-components
...
然後,將二進制插件 JAR 文件移動到一個方便的目錄中。在 Linux 中,默認目錄是 /opt/java/openjdk-binary-plugs。在 Solaris 中,默認目錄是 /usr/jdk/instances/openjdk-binary-plugs。在 Solaris 中,默認目錄是 C:\openjdk-binary-plugs. 可以將 JAR 置於您喜歡的其他位置,但是在構建之前,必須設置 ALT_BINARY_PLUGS_PATH 環境變量指向此位置。
將僅用於構建的二進制插件分布到太多位置是一種愚蠢的做法。由於這是一個開源項目,任何人都能修復問題,所以將此作為我們的第一個 TODO:
TODO:重寫構建文件,以便它首先查找其他源碼所在的標准 openjdk/binary-plugs 目錄。
事實上,二進制插件真正並沒有多大,因此只將其全部分布在第一個位置的源碼包中可能很明智。
解壓後的 openjdk 目錄包含一些 readme 文件和各個子項目的目錄,其中包括 jdk、hotspot、langtools、jaxws 和 jaxp。這些目錄應該能夠單獨構建 ,但是我曾這樣做,卻沒有成功。
生成 Make 文件
要構建 JDK,需要制作 jdk_generic_profile.sh 可執行腳本並運行它。在頂層 openjdk 目錄中,鍵入:
$ chmod +x ./jdk/make/jdk_generic_profile.sh
$ ./jdk/make/jdk_generic_profile.sh
多半可能,這將失敗。第一次我這樣做,得到如下消息:
WARNING: Cannot access ALT_BOOTDIR=/opt/java/jdk1.6.0
WARNING: Missing ALT_BINARY_PLUGS_PATH: /opt/java/openjdk-binary-plugs
我已經安裝了這些軟件,但是 makefile 沒有在預期的正確位置找到它們。ALT_BINARY_PLUGS_PATH 和 ALT_BOOTDIR 環境變量需要分別設置為 JDK 的安裝位置和二進制插件目錄。所以我這樣做了:
$ export ALT_BOOTDIR=/usr/local/java;
$ export ALT_BINARY_PLUGS_PATH=~/plugs
然後,jdk_generic_profile 腳本運行並創建 makefile。
完整性檢查
在下一個源碼庫中,可能有頂層 makefile, 但是如果使用 b23,則需要更改為 control/make 目錄。然後,在您的構建環境中使用 make sanity 進行完整性檢查:
$ cd control/make
$ make sanity
這將警告您一些忘記安裝的軟件和忘記設置的環境變量。下面是我的第一次完整性檢查的結果:
$ make sanity
/bin/sh: /usr/bin/gawk: not found
/bin/sh: /usr/bin/gawk: not found
/bin/sh: /NOT-SET/devtools/share/ant/latest/bin/ant: not found
/bin/sh: /NOT-SET/devtools/share/findbugs/latest/bin/findbugs: not found
../make/common/shared/Sanity-Settings.gmk:121: WARNING: ANT_VER should not be empty [Sanity-Settings.gmk]
../make/common/shared/Sanity-Settings.gmk:122: WARNING: FINDBUGS_VER should not be empty [Sanity-Settings.gmk]
../make/common/shared/Sanity-Settings.gmk:191: WARNING: TEMP_FREE_SPACE should not be empty [Sanity-Settings.gmk]
../make/common/shared/Sanity-Settings.gmk:192: WARNING: FREE_SPACE should not be empty [Sanity-Settings.gmk]
../build/linux-i586/tmp/alsaversioncheck.c:1:28: error: alsa/asoundlib.h: No such file or directory
../build/linux-i586/tmp/alsaversioncheck.c: In function 'main':
../build/linux-i586/tmp/alsaversioncheck.c:3: warning: incompatible implicit declaration of built-in function 'printf'
../build/linux-i586/tmp/alsaversioncheck.c:3: error: 'SND_LIB_VERSION_STR' undeclared (first use in this function)
../build/linux-i586/tmp/alsaversioncheck.c:3: error: (Each undeclared identifier is reported only once
../build/linux-i586/tmp/alsaversioncheck.c:3: error: for each function it appears in.)
make: *** [../build/linux-i586/tmp/alsaversioncheck] Error 1
我缺少了 gawk、ant、findbugs 和 ALSA。繼續安裝缺少的內容並重試。安裝完這些代碼段(使用 Synaptic)之後,構建仍然沒有找到 Ant,雖然 Ant 就在我的路徑中:
$ make sanity
/bin/sh: /NOT-SET/devtools/share/ant/latest/bin/ant: not found
/bin/sh: /NOT-SET/devtools/share/findbugs/latest/bin/findbugs: not found
../make/common/shared/Sanity-Settings.gmk:121: WARNING: ANT_VER should not be empty [Sanity-Settings.gmk]
...
$ /usr/bin/ant -v
Apache Ant version 1.7.0 compiled on August 29 2007
Buildfile: build.xml does not exist!
Build failed
我斷定如果嘗試設置 ANT_VER 將會行得通,但我已經對 Ant 做了足夠的工作,我猜想 ANT_HOME 可能是 make 腳本真正需要的。我試著設置 ANT_HOME 並再次運行完整性檢查:
$ export ANT_HOME=/usr/share/ant
$ make sanity
/bin/sh: /NOT-SET/devtools/share/findbugs/latest/bin/findbugs: not found
../make/common/shared/Sanity-Settings.gmk:122: WARNING: FINDBUGS_VER should not be empty [Sanity-Settings.gmk]
...
系統不再抱怨 Ant,但仍想運行 FindBugs。就個人而言,盡管我非常喜歡 FindBugs,但我發現代碼庫構建是否需要它是可質疑的。當前,我們應該試著移除依賴,而不是引入依賴。盡管如此,構建腳本需要它,因此我必須安裝。FindBugs 在 Synaptic 中不可用,因此我不得不手動安裝它。系統發出錯誤消息,抱怨:
../make/common/shared/Sanity-Settings.gmk:122: WARNING: FINDBUGS_VER should not be empty [Sanity-Settings.gmk]
因此,我將 FINDBUGS_VER 設置為 1.3.0。
$ export FINDBUGS_VER=1.3.0
這沒有起作用,因此我花了十五分鐘浏覽構建文件,並嘗試使用不同的值,直到我無意中發現了 FINDBUGS_HOME。將此環境變量設置為 FindBugs 的位置修復了這個問題。
$ export FINDBUGS_HOME=/opt/java/findbugs-1.3.0
TODO:如果問題是缺少 FINDBUGS_HOME 或 ANT_HOME 環境變量,則將得到上述錯誤消息,而非“FINDBUGS_VER/ANT_VER 為空”。較好的方法是完全移除對 FindBugs 的依賴。
下一個問題似乎是 Freetype。我又返回到 Synaptic 來安裝它。結果是,盡管默認情況下已經在 Ubuntu 中安裝了 Freetype,但開發的庫和頭文件還沒安裝。因此我需要去安裝 libfreetype6-dev 軟件包。這將成為其余安裝的公共課。如果您使用 Ubuntu 開發者配置而非基本桌面配置來開始,可能會碰到一點小麻煩。
無論如何,現在我們總算有些進展了。我承認,我還不能隨心所欲:
$ make sanity
make[1]: Entering directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
Bootstrap Settings:
BOOTDIR = /usr/local/java
ALT_BOOTDIR = /usr/local/java
BOOT_VER = 1.6 [requires at least 1.5]
OUTPUTDIR = ./../build/linux-i586
ALT_OUTPUTDIR =
ABS_OUTPUTDIR = /home/jars/openjdk/jdk/build/linux-i586
Build Tool Settings:
SLASH_JAVA = /NOT-SET
ALT_SLASH_JAVA =
VARIANT = OPT
JDK_DEVTOOLS_DIR = /NOT-SET/devtools
ALT_JDK_DEVTOOLS_DIR =
ANT_HOME = /usr/share/ant
FINDBUGS_HOME = /home/jars/findbugs-1.3.0
UNIXCOMMAND_PATH = /bin/
ALT_UNIXCOMMAND_PATH =
COMPILER_PATH = /usr/bin/
ALT_COMPILER_PATH =
DEVTOOLS_PATH = /usr/bin/
ALT_DEVTOOLS_PATH =
UNIXCCS_PATH = /usr/ccs/bin/
ALT_UNIXCCS_PATH =
USRBIN_PATH = /usr/bin/
ALT_USRBIN_PATH =
COMPILER_NAME = GCC
COMPILER_VERSION =
CC_VER = 4.1 [requires at least 3.2]
ZIP_VER = 2.32 [requires at least 2.2]
UNZIP_VER = 5.52 [requires at least 5.12]
ANT_VER = 1.7 [requires at least 1.6.3]
FINDBUGS_VER = 1.3 [requires at least 1.1]
TEMPDIR = ./../build/linux-i586/tmp
Build Directives:
OPENJDK = true
USE_HOTSPOT_INTERPRETER_MODE =
PEDANTIC =
DEV_ONLY =
NO_DOCS =
NO_IMAGES =
TOOLS_ONLY =
INSANE =
COMPILE_APPROACH = parallel
PARALLEL_COMPILE_JOBS = 2
ALT_PARALLEL_COMPILE_JOBS =
FASTDEBUG =
COMPILER_WARNINGS_FATAL = false
COMPILER_WARNING_LEVEL =
INCREMENTAL_BUILD = false
CC_HIGHEST_OPT = -O3
CC_HIGHER_OPT = -O3
CC_LOWER_OPT = -O2
CXXFLAGS = -O2 -fPIC -DCC_NOEX -W -Wall -Wno-unused
-Wno-parentheses -fno-omit-frame-pointer -D_LITTLE_ENDIAN
CFLAGS = -O2 -fno-strict-aliasing -fPIC -W -Wall
-Wno-unused -Wno-parentheses -fno-omit-frame-pointer -D_LITTLE_ENDIAN
BOOT_JAVA_CMD = /usr/local/java/bin/java -client -Xmx375m -Xms128m
-XX:PermSize=32m -XX:MaxPermSize=160m
BOOT_JAVAC_CMD = /usr/local/java/bin/javac
-J-XX:ThreadStackSize=768 -J-client -J-Xmx375m -J-Xms128m
-J-XX:PermSize=32m -J-XX:MaxPermSize=160m -encoding ascii
BOOT_JAR_CMD = /usr/local/java/bin/jar
BOOT_JARSIGNER_CMD = /usr/local/java/bin/jarsigner
JAVAC_CMD = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/bin/javac
-J-XX:ThreadStackSize=768 -J-client -J-Xmx375m -J-Xms128m
-J-XX:PermSize=32m -J-XX:MaxPermSize=160m -source 1.5 -target 5
-encoding ascii -Xbootclasspath:./../build/linux-i586/classes
JAVAH_CMD = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/bin/javah
-bootclasspath ./../build/linux-i586/classes
JAVADOC_CMD = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/bin/javadoc
-J-client -J-Xmx375m -J-Xms128m -J-XX:PermSize=32m -J-XX:MaxPermSize=160m
Build Platform Settings:
USER = jars
PLATFORM = linux
ARCH = i586
LIBARCH = i386
ARCH_FAMILY = i586
ARCH_DATA_MODEL = 32
ARCHPROP = i386
LINUX_VERSION = Unknown linux
ALSA_VERSION = 1.0.14a
OS_VERSION = 2.6.22-14-generic [requires at least 2.4.9-e.3]
OS_NAME = linux
TEMP_FREE_SPACE = 7515236
FREE_SPACE = 7515236
MB_OF_MEMORY = 503
GNU Make Settings:
MAKE = make
MAKE_VER = 3.81 [requires at least 3.78]
MAKECMDGOALS = sanity
MAKEFLAGS =
SHELL = /bin/sh
Target Build Versions:
JDK_VERSION = 1.7.0
MILESTONE = internal
RELEASE = 1.7.0-internal
FULL_VERSION = 1.7.0-internal-jars_17_nov_2007_21_32-b00
BUILD_NUMBER = b00
External File/Binary Locations:
USRJDKINSTANCES_PATH = /opt/java
BUILD_JDK_IMPORT_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries
ALT_BUILD_JDK_IMPORT_PATH =
JDK_IMPORT_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586
ALT_JDK_IMPORT_PATH =
LANGTOOLS_DIST =
ALT_LANGTOOLS_DIST =
CORBA_DIST =
ALT_CORBA_DIST =
JAXP_DIST =
ALT_JAXP_DIST =
JAXWS_DIST =
ALT_JAXWS_DIST =
HOTSPOT_DOCS_IMPORT_PATH = /NO_DOCS_DIR
ALT_HOTSPOT_DOCS_IMPORT_PATH =
HOTSPOT_IMPORT_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586
ALT_HOTSPOT_IMPORT_PATH =
HOTSPOT_CLIENT_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/jre/lib/i386/client
ALT_HOTSPOT_CLIENT_PATH =
HOTSPOT_SERVER_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/jre/lib/i386/server
ALT_HOTSPOT_SERVER_PATH =
CACERTS_FILE = ./../src/share/lib/security/cacerts
ALT_CACERTS_FILE =
CUPS_HEADERS_PATH = /usr/include
ALT_CUPS_HEADERS_PATH =
OpenJDK-specific settings:
FREETYPE_HEADERS_PATH = /usr/include
ALT_FREETYPE_HEADERS_PATH =
FREETYPE_LIB_PATH = /usr/lib
ALT_FREETYPE_LIB_PATH =
OPENJDK Import Binary Plug Settings:
BINARY_PLUGS_JARFILE = /home/jars/plugs/jre/lib/rt-closed.jar
ALT_BINARY_PLUGS_JARFILE =
BINARY_PLUGS_PATH = /home/jars/plugs
ALT_BINARY_PLUGS_PATH = /home/jars/plugs
BUILD_BINARY_PLUGS_PATH = /NOT-SET/re/jdk/1.7.0/promoted/latest/openjdk/binaryplugs
ALT_BUILD_BINARY_PLUGS_PATH =
PLUG_LIBRARY_NAMES =
Previous JDK Settings:
PREVIOUS_RELEASE_PATH = /NOT-SET/re/jdk/1.6.0/archive/fcs/bundles/linux-i586
ALT_PREVIOUS_RELEASE_PATH =
PREVIOUS_JDK_VERSION = 1.6.0
ALT_PREVIOUS_JDK_VERSION =
PREVIOUS_JDK_FILE = jdk-6-linux-i586.tar.gz
ALT_PREVIOUS_JDK_FILE =
PREVIOUS_JRE_FILE = jre-6-linux-i586.tar.gz
ALT_PREVIOUS_JRE_FILE =
PREVIOUS_RELEASE_IMAGE =
ALT_PREVIOUS_RELEASE_IMAGE =
WARNING: This machine appears to only have 503Mb of physical memory,
builds on this machine could be slow.
WARNING: LANG has been set to en_US.UTF-8, this can cause build failures.
Try setting LANG to "C".
WARNING: The directory HOTSPOT_IMPORT_PATH=/NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586
does not exist, check your value of ALT_HOTSPOT_IMPORT_PATH.
WARNING: HOTSPOT_IMPORT_PATH does not contain the interface file jvmti.h.
Check your value of ALT_HOTSPOT_IMPORT_PATH.
WARNING: File /NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/lib/sa-jdi.jar
does not exist. The JDI binding for the Serviceability Agent will not be
included in the build.
Please check your access to
/NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/lib/sa-jdi.jar
and/or check your value of ALT_HOTSPOT_IMPORT_PATH.
ERROR: You do not have access to valid Cups header files.
Please check your access to
/usr/include/cups/cups.h
and/or check your value of ALT_CUPS_HEADERS_PATH,
CUPS is frequently pre-installed on many systems,
or may be downloaded from http://www.cups.org
ERROR: HOTSPOT_CLIENT_PATH does not point to a valid HotSpot VM.
Please check your access to
/NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/jre/lib/i386/client/libjvm.so
and/or check your value of ALT_HOTSPOT_CLIENT_PATH.
ERROR: HOTSPOT_SERVER_PATH does not point to a valid HotSpot VM.
Please check your access to
/NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586/jre/lib/i386/server/libjvm.so
and/or check your value of ALT_HOTSPOT_SERVER_PATH.
Exiting because of the above error(s).
按順序繼續查看實際的錯誤消息,下一個問題似乎是默認情況下 Ubuntu 將 LANG 環境變量設置為 en_US.UTF-8,構建腳本想要設置為 C。Ubuntu 剛好在此。在 2007 年,UTF-8 任何人做任何事都應使用的惟一默認編碼。盡管如此,我們進行了修復並繼續前進:
$ export LANG=C
TODO: 弄清楚構建腳本為什麼堅持使用 C 作為 LANG。我懷疑兩種基本不同的屬性(自然語言和編程語言)在相同的環境變量名稱上發生了沖突。
下一個錯誤是:
HOTSPOT_IMPORT_PATH=/NOT-SET/re/jdk/1.7.0/promoted/latest/binaries/linux-i586 does not exist, check your value of ALT_HOTSPOT_IMPORT_PATH.
到底應該是什麼呢?正式構建指令 沒有提到。看起來實際上應該是 Java 7 的事,甚至沒有引導程序 JDK 的份。或許我在構建 JDK 之前必須構建 HotSpot?
又讀了 15 分鐘不同的博客之後,我發現 Sun 發布的構建指令是錯誤的(大吃一驚)。他們談論的“頂層 Makefile”根本不存在。相反,我必須在 control/make 目錄下運行 makefile 並應該構建一切:openjdk、hotspot 等等一切。我們來試一下:
$ make sanity
make[1]: Entering directory `/home/jars/openjdk/jdk/make'
make[2]: Entering directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
freetypecheck.c: In function 'main':
freetypecheck.c:45: warning: comparison is always false
due to limited range of data type
freetypecheck.c:54: warning: comparison is always false
due to limited range of data type
make[2]: Leaving directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
make[1]: Leaving directory `/home/jars/openjdk/jdk/make'
該死,我以為我已經修復了 freetype 問題。但是仔細一看,似乎這些消息只是針對 Sun 包括在構建中的 freetypecheck C 程序的一個問題,而非 freetype 本身有問題。相關的代碼行是:
if (strcmp(v, QUOTEMACRO(REQUIRED_FREETYPE_VERSION)) < 0) {
printf("Failed: headers are too old.\n");
}
and
if (strcmp(v, QUOTEMACRO(REQUIRED_FREETYPE_VERSION)) < 0) {
printf("Failed: too old library.\n");
}
似乎是如果這個測試總是失敗,那麼 freetype 就是好的,所以我們就忽略這個問題吧。
TODO: 弄清楚正在進行的問題並進行修復。
現在只剩下一個警告和一個錯誤:
WARNING: This machine appears to only have 503Mb of physical memory,
builds on this machine could be slow.
ERROR: You do not have access to valid Cups header files.
Please check your access to
/usr/include/cups/cups.h
and/or check your value of ALT_CUPS_HEADERS_PATH,
CUPS is frequently pre-installed on many systems,
or may be downloaded from http://www.cups.org
我已經為這台筆記本訂購了 4GB 的 RAM ,但貨還沒到。與此同時,我只能忍受緩慢的構建。但是,CUPS 可能是一個問題。返回到 Synaptic。好像又是一個設備安裝問題。似乎是我需要的 libcupsys2-dev。我安裝了並重試:
$ make sanity
make[1]: Entering directory `/home/jars/openjdk/jdk/make'
make[2]: Entering directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/jars/openjdk/jdk/make/tools/freetypecheck'
make[1]: Leaving directory `/home/jars/openjdk/jdk/make'
Build Machine Information:
build machine = jars-desktop
...
Previous JDK Settings:
PREVIOUS_RELEASE_PATH = /NOT-SET/re/jdk/1.6.0/archive/fcs/bundles/linux-i586
ALT_PREVIOUS_RELEASE_PATH =
PREVIOUS_JDK_VERSION = 1.6.0
ALT_PREVIOUS_JDK_VERSION =
PREVIOUS_JDK_FILE = jdk--linux-i586.tar.gz
ALT_PREVIOUS_JDK_FILE =
PREVIOUS_JRE_FILE = jre--linux-i586.tar.gz
ALT_PREVIOUS_JRE_FILE =
PREVIOUS_RELEASE_IMAGE =
ALT_PREVIOUS_RELEASE_IMAGE =
WARNING: This machine appears to only have 503Mb of physical memory,
builds on this machine could be slow.
Sanity check passed.
終於!在開始了大約 7 個小時之後,完整性檢查通過了。現在試著用 make 開始實際構建:
構建 Make 文件$ make
linux i586 1.7.0-internal build started: 07-11-17 22:33
/bin/mkdir -p ../../control/build/linux-i586/j2sdk-image
/bin/mkdir -p /home/jars/openjdk/control/build/linux-i586/j2sdk-image
...
# Running javac:
Check_ALT_JDK_IMPORT_PATH/bin/javac -J-XX:ThreadStackSize=768
-J-client -J-Xmx375m -J-Xms128m -J-XX:PermSize=32m
-J-XX:MaxPermSize=160m -source 1.5 -target 5 -encoding ascii
-classpath /usr/local/java/lib/tools.jar -sourcepath
/home/jars/openjdk/control/build/linux-i586/corba/gensrc:
../../../src/solaris/classes:
../../../src/share/classes -d
/home/jars/openjdk/control/build/linux-i586/corba/classes
@/home/jars/openjdk/control/build/linux-i586/corba/tmp/sun
/javax.transaction.xa/.classes.list
/bin/sh: Check_ALT_JDK_IMPORT_PATH/bin/javac: not found
make[3]: *** [.compile.classlist] Error 127
make[3]: Leaving directory `/home/jars/openjdk/corba/make/javax/xa'
make[2]: *** [build] Error 1
make[2]: Leaving directory `/home/jars/openjdk/corba/make/javax'
make[1]: *** [build] Error 1
make[1]: Leaving directory `/home/jars/openjdk/corba/make'
make: *** [corba-build] Error 2
嗯,好像是需要一個 ALT_JDK_IMPORT_PATH 環境變量。完整性檢查沒有捕獲到這一點。系統似乎正試著加載 javac。我不知道為什麼 JAVA_HOME 不夠好,但是我們可以嘗試只將其設置為正常的 JDK 目錄:
$ export ALT_JDK_IMPORT_PATH=/usr/local/java
現在構建似乎在前進。系統出現了許多不同類型的錯誤消息,比如:
../../../../../../../src/share/classes/org/omg/CORBA/ORB.java:593: warning:
non-varargs call of varargs method with inexact argument type for last parameter;
cast to java.lang.Object for a varargs call
cast to java.lang.Object[] for a non-varargs call and to suppress this warning
return (org.omg.CORBA.NVList)meth.invoke(this, argx);
似乎並非 JDK 中的所有代碼都要遵循最新的 Sun 編碼規范。但是,這僅僅是警告。代碼仍在構建。
TODO:修復此問題以獲得無警告的構建。
構建機器上的風扇/硬盤驅動器現在已過度疲勞了,整個房間都可以聽到旋轉的噪音。這只是一台筆記本。我希望它不要燒壞。如果它燒壞了,至少我還在 90 天的保修期內。好了,它安靜了一點。哇。噢,等一下。它安靜下來的原因是因為它出錯了:
/home/jars/openjdk/hotspot/agent/src/share/classes/sun/jvm
/hotspot/debugger/remote/x86/RemoteX86Thread.java:42:
cannot find symbol
symbol : class RemoteX86ThreadContext
location: class sun.jvm.hotspot.debugger.remote.x86.RemoteX86Thread
RemoteX86ThreadContext context = new RemoteX86ThreadContext(debugger);
^
/home/jars/openjdk/hotspot/agent/src/share/classes/sun/jvm/hotspot
/debugger/remote/x86/RemoteX86Thread.java:42: cannot find symbol
symbol : class RemoteX86ThreadContext
location: class sun.jvm.hotspot.debugger.remote.x86.RemoteX86Thread
RemoteX86ThreadContext context = new RemoteX86ThreadContext(debugger);
^
注意:/home/jars/openjdk/hotspot/agent/src/share/classes/sun/jvm/hotspot/jdi/SAJDIClassLoader.java uses or overrides a deprecated API.
注意:Recompile with -Xlint:deprecation for details.
2 errors
make[6]: *** [/home/jars/openjdk/control/build/linux-i586/hotspot/outputdir
/linux_i486_compiler2/product/../generated/sa-jdi.jar] Error 1
make[6]: Leaving directory
`/home/jars/openjdk/control/build/linux-i586/hotspot/outputdir
/linux_i486_compiler2/product'
make[5]: *** [all] Error 2
make[5]: Leaving directory
`/home/jars/openjdk/control/build/linux-i586/hotspot/outputdir
/linux_i486_compiler2/product'
make[4]: *** [sa_stuff] Error 2
make[4]: Leaving directory
`/home/jars/openjdk/control/build/linux-i586/hotspot/outputdir
/linux_i486_compiler2/product'
make[3]: *** [product] Error 2
make[3]: Leaving directory
`/home/jars/openjdk/control/build/linux-i586/hotspot/outputdir'
make[2]: *** [generic_build2] Error 2
make[2]: Leaving directory `/home/jars/openjdk/hotspot/make'
make[1]: *** [product] Error 2
make[1]: Leaving directory `/home/jars/openjdk/hotspot/make'
make: *** [hotspot-build] Error 2
但仍有進展。至少這像是一個真正的 Java 缺陷,而不是環境問題或 C 缺陷。指定的缺陷似乎是沒有可訪問的把 RemoteThreadContext 構造器,該構造器接受 RemoteDebuggerClient 作為參數。打開 RemoteX86Thread.java 來查看可能正在發生的事情,我沒查到任何問題,但是當我打開 RemoteThreadContext.java 時,問題顯而易見:文件是空的!我想知道發生了什麼。
當我解壓或下載原始文件時,也許一些事情中斷操作了。我又弄到了一個新的副本,它似乎包含 RemoteThreadContext.java,因此我將其完全復制到它該在的位置,並再次運行 make。這一次,在 make 中止之前,我設法在 Arathi Basin 中完整地玩了一圈:
Timing: 00000 seconds or 0s for make-java-jvm
<<<Finished Recursively making jvm all @ Sat Nov 17 23:54:14 CET 2007.
>>>Recursively making redist all @ Sat Nov 17 23:54:14 CET 2007 ...
make[3]: Entering directory `/home/jars/openjdk/jdk/make/java/redist'
BinaryPlugs import started: Sat Nov 17 23:54:14 CET 2007
BINARY_PLUGS_PATH=/home/jars/plugs
make[3]: *** No rule to make target `/home/jars/plugs/jre/lib/i386/libjsoundhs.so', needed by `/home/jars/openjdk/control/build/linux-i586/lib/i386/libjsoundhs.so'. Stop.
make[3]: Leaving directory `/home/jars/openjdk/jdk/make/java/redist'
make[2]: *** [all] Error 1
make[2]: Leaving directory `/home/jars/openjdk/jdk/make/java'
make[1]: *** [all] Error 1
make[1]: Leaving directory `/home/jars/openjdk/jdk/make'
make: *** [jdk-build] Error 2
也許是我沒有正確安裝插件?也許我必須將其 unjar?不。不會是一個惡作劇吧。等一下:它是一個自運行的 JAR:
$ java -jar jdk-7-ea-plug-b23-linux-i586-30_oct_2007.jar
Error: Install failed: java.awt.HeadlessException:
No X11 DISPLAY variable was set, but this program performed
an operation which requires it.
java.awt.HeadlessException:
No X11 DISPLAY variable was set, but this program performed
an operation which requires it.
at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:159)
...
嗯,我猜我不能在控制台中運行它。我必須把椅子轉向筆記本,在它上面運行。
TODO:讓二進制插件安裝程序能夠無頭運行。
好了。現在我在 /home/jars/plugs/openjdk-binary-plugs 中找到了未壓縮的二進制插件。我需要更新環境變量以再次匹配 make:
$ export ALT_BINARY_PLUGS_PATH=/home/jars/openjdk-binary-plugs
$ make
Make 再次運行,但不久在另一個位置中止了:
In file included from
/home/jars/openjdk/jdk/src/share/native/sun/awt/../java2d/pipe/Region.h:34,
from /home/jars/openjdk/jdk/src/share/native/sun/awt/../java2d/pipe/Region.c:30:
/home/jars/openjdk/jdk/src/solaris/native/sun/awt/utility/rect.h:31:22: error:
X11/Xlib.h: No such file or directory
In file included from
...
/home/jars/openjdk/jdk/src/solaris/native/sun/awt/img_util_md.h:32:
error: expected specifier-qualifier-list before 'XID'
/home/jars/openjdk/jdk/src/share/native/sun/awt/image/BufImgSurfaceData.c:
In function 'Java_sun_awt_image_BufImgSurfaceData_freeNativeICMData':
/home/jars/openjdk/jdk/src/share/native/sun/awt/image/BufImgSurfaceData.c:95:
warning: cast to pointer from integer of different size
make[4]: ***
[/home/jars/openjdk/control/build/linux-i586/tmp/sun/sun.awt/awt/obj/BufImgSurfaceData.o]
Error 1
make[4]: Leaving directory `/home/jars/openjdk/jdk/make/sun/awt'
make[3]: *** [library_parallel_compile] Error 2
make[3]: Leaving directory `/home/jars/openjdk/jdk/make/sun/awt'
make[2]: *** [all] Error 1
make[2]: Leaving directory `/home/jars/openjdk/jdk/make/sun'
make[1]: *** [all] Error 1
make[1]: Leaving directory `/home/jars/openjdk/jdk/make'
make: *** [jdk-build] Error 2
也許是我丟掉了一些 X11 開發庫?返回到 Synaptic。讓我們安裝 libx11-dev 並重試。不,不能那樣做。看起來像是 libxt-dev 的一個小 Googling 是缺少的部分。我次我又前進了一點。現在又缺少了另一個文件:
/home/jars/openjdk/jdk/src/solaris/native/sun/awt/splashscreen/splashscreen_config.h:33:34: error: X11/extensions/shape.h: No such file or directory.
這次我直接轉到 Google,發現“包括文件是非矩形窗口形狀擴展標准的一部分”。似乎 libext-dev 是我需要的軟件包。又一次突破。
我想這可能是最後一個了。我似乎已經編譯了所有的東西。當然,這是 C 而不是 Java,這並不意味著我完成了。現在出現了鏈接器錯誤:
/usr/bin/ld: cannot find -lXtst
因此我安裝 libxtst-dev 頭文件。
越來越煩人了。我僅列出我必須安裝的其他庫:
libXi-dev
嗯,可能就是它。現在它似乎正在生成 JavaDoc。有許多已破壞的但容易固定的 JavaDoc 標記,但是我想實際上已經完成了。現在,我只能弄清楚構建將每件東西放在哪裡。:-)
安裝
輸出似乎位於 openjdk/control/build/linux-i586/j2sdk-image。(其他一些構建產品,比如無開發工具的 JRE,也位於 openjdk/control/build/linux-i586/。) 試著將其復制到 /opt/java 中,設置為 JAVA_HOME,並將其添加到以下路徑中:
$ sudo cp -R j2sdk-image /opt/java
$ export JAVA_HOME=/opt/java
$ export PATH=/opt/java/bin:$PATH
現在到了實現真理的時刻了:
$ java -version
openjdk version "1.7.0-internal"
OpenJDK Runtime Environment (build 1.7.0-internal-jars_18_nov_2007_01_03-b00)
OpenJDK Client VM (build 12.0-b01, mixed mode)
$ javac -version
javac 1.7.0-internal
成功了!此時正好是上午 12:32 ,我大約在 10:00(上午而非下午)左右開始;但畢竟完成了。既然已經安裝了所有正確的庫,也許下一個安裝只用七個小時就夠了。
更簡單的方法
帶有 make 的原始構建對於自動化、測試、接口和連續集成是十分重要的。然而,對於日復一日的開發,它們通常都不是最容易的方法。如果這對於您來說似乎太繁瑣的話,那麼還有其他方法,但它們都有各自的類似問題。
預建的二進制軟件包
如果您覺得調試 makefile 不是度過周末的好方法,那麼您可能就需要從 JDK 7 二進制快照頁面 中安裝預建的二進制軟件包。
IDE
我已經集中從命令行開始構建了,因為這是最通用的且能交互操作的方法。它還使得自動化和測試更加容易。命令行構建應該被所有好的開源軟件支持。也就是說,有時 IDE 也有用。Sun 積極鼓勵使用 NetBeans 來構建和修改 JDK,而且 openjdk 下載附帶了預配置的 NetBeans 項目 openjdk/jdk/make/netbeans。僅在 NetBeans 中打開並運行。更多指令,請訪問 NetBeans 網站。一定要小心,這些指令並不完全准確,或者您可能仍需進行一些調試以達到完全構建,甚至是在 NetBeans 中。
結束語
我們學到了什麼?可以構建 JDK 了。第一次做這件事僅花費一天或兩天的時間,而且熟練一下 Unix 和 C 庫不是一件壞事。希望它能夠讓您比我花費更少的時間。從現有 Ubuntu 桌面配置開始,您需要進行如下操作:
從 Sun 中安裝最新的 JDK 6。(Ubuntu 僅附帶 JRE。)
從 OpenJDK 源碼發布頁面 下載源碼包並解壓,以創建 openjdk 目錄。
從同一頁面下載二進制插件 JAR 並運行以創建 openjdk-binary-plugs 目錄。
安裝以下軟件包:
gawk
ant
findbugs
ALSA
libcupsys2-dev
libext-dev
libXi-dev
libxt-dev
libxtst-dev
手動安裝 FindBugs
設置以下環境變量:
ALT_BOOTDIR= JDK 6 的安裝位置
ALT_BINARY_PLUGS_PATH= 二進制插件的任意安裝位置
ANT_HOME= Ant 的任意安裝位置
FINDBUGS_HOME= wherever 的任意安裝位置
ALT_JDK_IMPORT_PATH= JDK 6 的安裝位置
LANG=C
生成 openjdk/jdk/make/jdk_generic_profile.sh 可執行文件並運行。
移至 openjdk/control/make 並鍵入 make sanity。調試所發生的任何問題。
通過完整性檢查之後,請鍵入 make。去拿一杯咖啡。這將需要一段時間。
構建完成之後,請將 openjdk/control/build/linux-i586/j2sdk-image 目錄復制到您想放置 JDK 的任意位置。/opt/java7 目錄可能是一個不錯的選擇。
在其他 Linux 發行版中,可能需要添加 Ubuntu 默認打包的另外一些庫,或者可能不需要添加此處列出的所有庫。在其他操作系統(比如 Solaris)中,可能需要指定 gnumake 而非 make(gnumake 是 Ubuntu 上的默認 make)。在 Windows 中,祝您好運。我確信它能夠完成,但是我沒有完整的一天來調試這一環境了。也許會在另一篇文章中完成這些。