羊老姆上線:抄起鍵盤就編譯JDK源碼,結果上頭了

image.png


好奇害死羊

不少小夥伴們作Java開發,每天寫Java代碼,確定離不開Java基礎環境:JDK,畢竟咱們寫好的Java代碼也是跑在JVM虛擬機上。java

通常來講,咱們學Java以前,第一步就是安裝JDK環境。這個簡單啊,咱們通常直接把JDK從官網下載下來,安裝完成,配個環境變量就能夠愉快地使用了。git

不過話說回來,對於這個每天使用的東西,咱們難道很差奇這玩意兒它究竟是怎麼由源碼編譯出來的嗎?macos

帶着這個原始的疑問,今天準備大幹一場,本身動動呆萌的小手,來編譯一個屬於本身的JDK吧!bootstrap


還有個待填的坑

記得以前不是出過一期關於《JDK源碼閱讀環境搭建》相關的視頻以及文章嘛,細心的小夥伴,可能會發現一個很實際的問題網絡

咱們將src.zip包裏的JDK源碼解壓出來,關聯到這份源碼以後,調試時是能夠進,可是咱們在加註釋的時候卻只能在行尾添加,並不能改變原代碼的行結構。換句話說,若是在源碼中加了跨行的多行註釋,則debug調試的時候就會出現當前行的運行錯位問題,這個有點尷尬了。app

固然那個視頻的評論區,的確也有幾個小夥伴提了這個問題:ide

image.pngimage.png

緣由也很簡單,由於實際支撐調試運行的代碼,並非咱們解壓出來的那份JDK源碼,那個僅僅是作關聯用,實際運行用到的JDK,仍是以前系統安裝好的那個JDK環境。函數

要想解決這個問題,那就只能使用本身修改過的代碼來自行編譯生成本身的JDK,而後用到項目中去!工具

因此什麼都憋說了,肝就完了!ui


環境準備

首選說在前面的是,編譯前的軟件版本關係極其重要,本身在踩坑時,所出現的各類奇奇怪怪的問題幾乎都和這個有關,後來版本匹配以後,就很是順利了。

咱們來盤點和梳理一下編譯一個JDK須要哪些環境和工具:

一、boot JDK

咱們要想編譯JDK,首先本身本機必須提早已經安裝有一個JDK,官方稱之爲bootstrap JDK(或者稱爲boot JDK)。

好比想編譯JDK 8,那本機必須最起碼得有一個JDK 7或者更新一點的版本;你想編譯JDK 11,那就要求本機必須裝有JDK 10或者11

因此雞生蛋、蛋生雞又來了...

二、Unix環境

編譯JDK須要Unix環境的支持!

這一點在Linux操做系統和macOS操做系統上已經自然的保證了,而對於Windows兄弟來講稍微麻煩一點,須要經過使用Cygwin或者MinGW/MSYS這種軟件來模擬。

就像官方所說:在Linux平臺編譯JDK通常問題最少,容易成功;macOS次之;Windows上則須要稍微多花點精力,問題可能也多一些。

究其本質緣由,仍是由於Windows畢竟不是一個Unix-Like內核的系統,畢竟不少軟件的原始編譯都離不開Unix Toolkit,因此相對確定要麻煩一些。

三、編譯器/編譯工具鏈

JDK底層源碼(尤爲JVM虛擬機部分)不少都是C++/C寫的,因此相關編譯器也跑不掉。

一圖勝千言,各平臺上的編譯器支持以下表所示,按平臺選擇便可:

image.png

四、其餘工具

典型的好比:

  • Autoconf:軟件源碼包的自動配置工具
  • Make:編譯構建工具
  • freetype:一個免費的渲染庫,JDK圖形化部分的代碼可能會用它

好,環境盤點就到這裏,接下來具體列一下我在編譯JDK 8JDK 11時分別用到的軟件詳細版本信息:

編譯JDK 8時:

  • 操做系統:macOS 10.11.6
  • boot JDK:JDK 1.8.0 (build 1.8.0_201-b09)
  • Xcode版本:8.2
  • 編譯器:Version 8.0.0 (at /usr/bin/clang)

編譯JDK 11時:

  • 操做系統:macOS 10.15.4
  • boot JDK:JDK 11.0.7 (build 11.0.7+8-LTS)
  • Xcode版本:11.5
  • 編譯器:Version 11.0.3 (at /usr/bin/clang)

你們在編譯時若是過程當中有不少問題,大機率少軟件沒裝,或者軟件版本不匹配,不要輕易放棄,須要耐心自查一下。


下載JDK源碼

下載JDK源碼其實有兩種方式。

方式一:經過Mercurial工具下載

Mercurial能夠理解爲和Git同樣,是另一種代碼管理工具,安裝好以後就有一個hg命令可用。

image.png

OpenJDK的源碼已經提早託管到http://hg.openjdk.java.net/

所以,好比下載JDK 8,可直接hg clone一下就行,和git clone同樣:

hg clone http://hg.openjdk.java.net/jdk8/jdk8

同理,下載JDK 11

hg clone http://hg.openjdk.java.net/jdk/jdk11

可是這種方式下載速度不是很快。

方式二:直接下載打包好的源碼包

下載地址:https://jdk.java.net/

image.png

選擇你想要的版本下載便可。


編譯前的自動配置

源碼包下載好,放到本地某個目錄(建議路徑純英文,避免沒必要要的麻煩),解壓之,而後進入源碼根目錄,執行:

sh configure

固然這裏運行的是默認配置項。

這一步會進行一系列的自動配置工做,時間通常很快,最終若是能出現一下提示,那麼很幸運,編譯前的配置工做就完成了!

這裏我給出我本身分別在配置JDK 11JDK 8時候完成時的樣子:


配置JDK 8完成:

image.png

配置JDK 11完成:

image.png

注: 若是這一步出錯,大機率是某個軟件環境未裝,或者即便裝了,但版本不匹配,控制檯打印日誌裏通常是會提醒的。

好比我在配置JDK 8的時候,就遇到了一個errof:GCC compiler is required的問題:

image.png

明明系統裏已經有編譯器,但仍是報這個錯誤。經過後來修改 jdk源碼根目錄/common/autoconf/generated-configure.sh文件,將相關的兩行代碼註釋後就配置經過了

image.pngimage.png

配置完成,接下來開始執行真正的編譯動做了!


真正的編譯動做

咱們這裏進行的是全量編譯,直接在咱們下載的JDK源碼根目錄下執行以下命令便可:

make all

這一步編譯須要一點時間,耐心等待一下便可。編譯過程若是有錯誤,會終止編譯,若是能看到以下兩個畫面,那麼則恭喜你,本身編譯JDK源碼就已經經過了,能夠搞一杯咖啡慶祝一下了。

JDK 8編譯完成:

image.png

JDK 11編譯完成:

image.png

從兩張圖的對比能夠看出,編譯JDK 8JDK 11完成時在輸出上仍是有區別的。時間上的區別很大程度上來源於JDK 11的編譯機配置要高很多。


驗證成果

JDK源碼編譯完成以後確定會產生和輸出不少產物,這也是咱們所火燒眉毛想看到的。

因爲JDK 8JDK 11的源碼包組織結構並不同,因此輸出東西的內容和位置也有區別。咱們一一來盤點一下。

一、JDK 8的編譯輸出

編譯完成,build目錄下會生成一個macosx-x86_64-normal-server-release目錄,全部的編譯成果均位於其中。

首先,編譯出來的Java可執行程序能夠在以下目錄裏找到:

jdk源碼根目錄/build/macosx-x86_64-normal-server-release/jdk/bin

進入該目錄後,能夠輸入./java -version命令驗證:

image.png

其次,編譯生成的成品JDK套裝,能夠在目錄

jdk源碼根目錄/build/macosx-x86_64-normal-server-release/images

下找到,如圖所示:

image.png

其中:

  • j2sdk-image:編譯生成的JDK
  • j2re-image:編譯生成的JRE

進入j2sdk-image目錄會發現,裏面的內容和咱們平時從網絡上下載的成品JDK內容一致。

image.png

二、JDK 11的編譯輸出

JDK 11的源碼目錄組織方式和JDK 8自己就有區別,編譯生成的產物和上面編譯JDK 8的輸出有必定區別,但也不大。

JDK 11編譯完成,一樣在build目錄下會生成一個macosx-x86_64-normal-server-release目錄,全部的編譯成果均位於其中。

一樣編譯出來的Java可執行程序能夠在目錄

JDK源碼根目錄/build/macosx-x86_64-normal-server-release/jdk/bin

下看到,進入該目錄後,也能夠輸入./java -version命令驗證:

image.png

其次,編譯生成的成品JDK 11套裝,能夠在目錄

JDK源碼根目錄/build/macosx-x86_64-normal-server-release/images

下找到,如圖所示:

image.png

其中jdk目錄就是編譯生成的成品JDK 11套裝。


使用本身編譯的JDK

既然咱們已經動手編譯出了JDK成品,接下來咱們得用上哇。

新建一個最最基本的Java工程,好比命名爲JdkTest,目的是把咱們本身編譯出的JDK給用上。

image.png

咱們點開Project Structure,選到SDKs選項,新添加上本身剛剛編譯生成的JDK,並選爲項目的JDK,看看是否能正常工做

image.pngimage.png

點擊肯定以後,咱們運行之:

image.png

能夠看到咱們本身編譯出的JDK已經用上了。


關聯JDK源碼並修改

咱們繼續在上一步JdkTest項目的Project Structure → SDKs裏將JDK源碼關聯到自行下載的JDK源碼路徑上:

image.png

這樣方便咱們對本身下載的JDK源碼進行閱讀調試修改、以及在源碼裏隨意作筆記加註釋

舉個最簡單的例子,好比咱們打開System.out.println()這個函數的底層源碼:

image.png

咱們隨便給它修改一下,加兩行簡單的標記,像這樣:

image.png

爲了使咱們新加的代碼行生效,咱們必需要從新去JDK源碼的根目錄中再次執行 make images從新編譯生成JDK方可生效:

圖片

由於以前已經全量編譯過了,因此再次make的時候增量編譯通常很快。

從新編譯以後,咱們再次運行JdkTest項目,就能夠看到改動的效果了:

image.png


多行註釋的問題

記得以前搭建《JDK源碼閱讀環境》時,你們可能發現了一個問題:閱讀源碼嘛,給源代碼作點註釋或筆記很常見!但那時候有個問題就是作註釋時不可改變代碼的行結構(只能行尾註釋,不能跨行註釋),不然debug調試時會出現行號錯位的問題。

緣由很簡單,由於咱們雖然作了源代碼目錄的映射,可是實際支撐運行的JDK仍是預先安裝好的那個JDK環境,並非根據咱們修改後的源碼來從新編譯構建的,因此看到這裏,解決這個問題就很簡單,就像上面同樣自行編譯一下JDK便可。

實際在實驗時,還有一個很典型的問題是,當添加了多行的中文註釋後,再編譯竟然會報錯!

好比,仍是以上面例子中最簡單的System.out.println()源碼爲例,咱們添加幾行中文註釋:

image.png

這時候咱們去JDK源碼目錄下編譯會發現滿屏相似這樣的報錯:

錯誤: 編碼 ascii 的不可映射字符

image.png

頓時有點懵,畢竟僅僅是加了幾行註釋。對於咱們來講,源碼裏寫點多行的中文註釋基本是剛需,然而編譯竟會報錯,這還能不能讓人愉快的玩耍了... 當時後背有點發涼。

實不相瞞,就這個問題排查了一段時間,熬到了很晚。最終折騰了一番,經過以下這種方式解決了,順便分享給小夥伴們,你們若是遇到了這個問題,能夠參考着解決一下。

由於從控制檯的報錯能夠很明顯的看出,確定是字符編碼相關的問題致使的,並且都指向了ascii這種編碼方式。

因而將JDK的源碼從根目錄導入了Vs Code,而後全目錄查找encoding ascii相關的內容,看看有沒有什麼端倪,結果發現

jdk源碼根目錄/make/common/SetupJavaCompilers.gmk文件中有兩處指定了ascii相關的編碼方式:

image.png

因而嘗試將這兩處-encoding ascii的均替換成-encoding utf-8

image.png

而後再次執行make images編譯,編譯順利經過!

image.png

至此大功告成!

這樣後面不論是閱讀調試仍是定製JDK源碼都很是方便了。


此次分享就到這裏吧,小夥伴們下篇見!

天天進步一點點

慢一點才能更快


spacer.gif

相關文章
相關標籤/搜索