從事軟件開發(fā)的行家里手們對版本發(fā)布流程與節(jié)奏如此了若指掌誊册,以至于他們將其精髓內(nèi)化(internalize)并以為人人都懂得這些“淺顯的道理”已维。 可是事實(shí)恰好相反,外行一眼望去如同霧里看花。 所以為了整個Julia社區(qū)趾访,乃至于其它編程語言社區(qū),我覺得有必要將Julia的開發(fā)過程白紙黑字地寫下來董虱。 在本文中扼鞋,我將闡述:
各種不同的版本
各種版本中允許和不允許的改動
版本發(fā)布流程的各階段
根據(jù)風(fēng)險承受力決定使用哪種版本
發(fā)布流程中的各階段與標(biāo)志性事件
這些文字材料是從discourse論壇和Slack協(xié)作交流群中摘錄而來申鱼。 所有資料都是現(xiàn)成的,我只是將其歸納在一處云头。 如果大家覺得這篇文章頗有益處捐友,我們會考慮將其變成一份官方文檔。 宏觀上來說溃槐,Julia遵循SemVer標(biāo)準(zhǔn)制定的“語義化版本”匣砖。 但SemVer在微觀上提供了許多自由度,供使用者自行解釋昏滴。 這篇文章正是為填補(bǔ)這些微觀細(xì)節(jié)所作猴鲫。
補(bǔ)丁版本(Patch releases)
SemVer的版本號格式為主版本號.次版本號.修訂號。 Julia的補(bǔ)丁版本增加版本號的最后位谣殊,即修訂號拂共。 比方說,從1.2.3到1.2.4標(biāo)志著補(bǔ)丁版本的發(fā)布姻几。
依據(jù)SemVer宜狐,補(bǔ)丁版本只能包含bug修復(fù),低風(fēng)險的性能改進(jìn)蛇捌,和文檔更新抚恒。 當(dāng)然,對于什么才是bug修復(fù)络拌,不同的人有不同的見解俭驮。 造成這一分歧的原因是有些人誤將bug當(dāng)feature并寫出建構(gòu)在其上的代碼。 總體來說春贸,我們發(fā)布補(bǔ)丁時會慎之又慎表鳍,并且用PkgEval1來確保盡可能少的既有代碼遇到兼容性問題。 有理由相信祥诽,用戶們可以高枕無憂地更新到最新的補(bǔ)丁版本譬圣。
我們認(rèn)為,如果不是為了修復(fù)某個bug雄坪,補(bǔ)丁版本也應(yīng)當(dāng)避免修改內(nèi)部代碼厘熟。 盡管通常來說在任何版本中,做出公開應(yīng)用程式介面(API)以外的修改都是無可厚非的维哈,我們?nèi)灾?jǐn)慎地避免這一行為绳姨,以將不兼容的風(fēng)險降到最低。
一般來說阔挠,補(bǔ)丁版本大約每月發(fā)布一次飘庄,并建立在當(dāng)前的幾個活躍(active)版本分支(branch)上(稍后詳述)。 如果當(dāng)月湊不齊足夠的bug修復(fù)购撼,該月也可能被跳過跪削。
大約在發(fā)布補(bǔ)丁版本的五天前谴仙,我們會在反向移植(backport)分支上運(yùn)行PkgEval。 如果一切順利碾盐,我們會將其歸并(merge)并凍結(jié)(freeze)這些版本分支晃跺,并在discourse上宣布可以開始測試了。 如果在這接下來的五天里毫玖,一切風(fēng)平浪靜掀虎,這些版本分支會被貼上新的版本標(biāo)簽(tag)。
次要版本(Minor releases)
次要版本增加版本號的中間位付枫,即次版本號烹玉。 比方說,從1.2.3到1.3.0標(biāo)志著次要版本的發(fā)布阐滩。
次要版本包含bug修復(fù)二打,新特性(feature),和一些“小改動”叶眉。 這些小改動理論上可能造成不兼容,但事實(shí)上很少引起不兼容芹枷。 更何況衅疙,我們通過PkgEval完全避免了不兼容的發(fā)生。
次要版本也會大量地重構(gòu)(refactor)內(nèi)部代碼鸳慈。 之前提到饱溢,我們規(guī)定補(bǔ)丁版本只允許在修復(fù)bug的前提下小范圍地重構(gòu)內(nèi)部代碼,次要版本便順理成章地成為我們大范圍重構(gòu)的工作場所走芋。 如果你的程序依賴于我們的內(nèi)部代碼而不是公開的應(yīng)用程式介面的話绩郎,你這下可能會遇到不兼容問題。 事實(shí)上翁逞,你之所以在補(bǔ)丁版本僥幸活了下來肋杖,是因?yàn)槲覀冊谘a(bǔ)丁版本執(zhí)行了比SemVer更嚴(yán)格的標(biāo)準(zhǔn)。 任何出于某種需要而依賴我們內(nèi)部代碼的用戶挖函,在升級次要版本時都應(yīng)該格外小心状植。
次要版本每四月發(fā)布一次,也就是每年發(fā)布三次怨喘。 每四個月津畸,我們在discourse上宣布當(dāng)前開發(fā)版本將在兩周后凍結(jié)。 在凍結(jié)當(dāng)天必怜,我們?yōu)榇我姹窘elease-1.3分支2肉拓。 該分支會被貼上版本標(biāo)簽,并且不允許額外添加新增特性梳庆。
主要版本(Major releases)
主要版本增加版本號的第一位暖途,即主版本號卑惜。比方說,2.0.0標(biāo)志著主要版本的發(fā)布丧肴。
依據(jù)SemVer残揉,主要版本可以大刀闊斧地改動。 不過芋浮,現(xiàn)實(shí)中抱环,我們很清楚我們將如何塑造Julia的代碼,并不會做出面目全非的改變纸巷。 大部分用戶級別代碼會在Julia?2.0版本3中完整地保留下來镇草。 無理取鬧地將一切規(guī)則打破并不是我們想看到的。
主要版本的職責(zé)在于修正明顯的API設(shè)計(jì)缺陷瘤旨,人人都會因?yàn)槟軘[脫這種差勁的梯啤,撲朔迷離的API而拍手稱快。 主要版本也允許修改底層代碼存哲,這會造成某些第三方庫(package)的不兼容因宇,但這是從根本上改善語言所必須付出的代價。
長期支持(Long term support)
一些用戶樂于時刻更新Julia以獲得最炫最酷的新特性祟偷。 另一些用戶甚至樂此不疲地每天重新編譯Julia的master分支以做第一個吃螃蟹的人察滑。 還有的用戶,恰恰相反修肠,一年到頭也懶得升級一次贺辰。 理想情況下,我們愿意為每個存在于世的次要版本永遠(yuǎn)提供bug修復(fù)服務(wù)嵌施。 如果我們有無限的資源饲化,我們會將每一個bug修復(fù)反向移植到每一個兼容的版本分支上。 理想很豐滿吗伤,現(xiàn)實(shí)很骨感吃靠。 我們的資源僅夠我們維護(hù)幾個活躍版本分支。 因此足淆,我們退而求其次撩笆,決定在任何時間節(jié)點(diǎn)上僅維護(hù)至多四個活躍分支:
master分支: 這是所有新特性的發(fā)源地,大部分bug修復(fù)的棲息之處缸浦,也將在未來成為有劃時代意義的2.0版本的搖籃夕冲。
不穩(wěn)定版本(unstable release)分支(當(dāng)前為release-1.3):在這個分支上,新特性已經(jīng)被凍結(jié)下來裂逐,但bug修復(fù)和性能改進(jìn)仍被允許坎背。 通常泛啸,bug修復(fù)先在master上完成并隨后反向移植到該分支册烈。 時機(jī)成熟后,該分支將被貼上版本標(biāo)簽(當(dāng)前為1.3.0)南片,并以新的穩(wěn)定版本分支這一身份活躍。 不穩(wěn)定版本分支并不是一直存在的:它只存在于特性凍結(jié)(feature freeze)后庭敦,下個次要版本前疼进。 在此之后它都不會露面,直到四個月之后的下一次特性凍結(jié)秧廉。
穩(wěn)定版本(stable release)分支(當(dāng)前為release-1.2):這個版本分支跟蹤最新發(fā)布的次要(或主要)版本伞广。 這個分支永遠(yuǎn)存在,并且通過反向移植從master獲得一切可用的bug修復(fù)疼电。 未來的補(bǔ)丁版本(比方說1.2.1)會建立在這個分支上嚼锄。 當(dāng)不穩(wěn)定版本分支上位為新的穩(wěn)定版本分支時,該舊有穩(wěn)定版本分支將被遺棄蔽豺。
長期支持?(long term support区丑, LTS)分支(當(dāng)前為release-1.0):這個稍舊的版本分支在其生命周期內(nèi)將持續(xù)地獲得bug修復(fù)。 縱使某些bug修復(fù)不能完全地反向移植到該分支修陡,我們也會額外花力氣妥善地修復(fù)該分支上的bug沧侥。 一個舊LTS分支將在另一個分支成為新LTS分支時退役。
現(xiàn)在問題只剩一個:什么時候LTS分支會更替魄鸦??release-1.0是我們目前唯一確立過的LTS分支宴杀。 獲得了四個補(bǔ)丁版本后,該分支相當(dāng)穩(wěn)定并被廣泛支持号杏。 可是婴氮,它從master獲得的bug修復(fù)補(bǔ)丁會與日俱減斯棒,并且越來越多的第三方庫會放棄對其的支持(這些庫需要用到Julia新版本中的特性)盾致。 當(dāng)合適的時機(jī)來臨時,我們不得不選擇一個新的LTS分支并宣布放棄維護(hù)1.0.x系列荣暮。 這個新的LTS分支可能是1.4或1.8庭惜,也可能是2.0。 我們現(xiàn)在還無法預(yù)言穗酥,但這一天終究會到來护赊。 幸運(yùn)的是,即便如此砾跃,1.0.x系列的使用者們也并非一定要升級版本骏啰。 他們大可以使用這一舊版本,并只與和該版本兼容的第三方庫版本打交道抽高。 到那時判耕,它將成為最穩(wěn)定,最充分測試的Julia版本翘骂。 所以壁熄,只要你不需要新特性帚豪,你大可以放心地繼續(xù)無限期地使用它。 另外草丧,如果某人或某組織出于自身利益狸臣,愿意繼續(xù)維護(hù)某個舊版本分支,也就是甄選(cherry-pick)反向移植并運(yùn)行PkgEval以確保兼容性昌执,我們將很樂意接受這些幫助從而發(fā)布更多的版本烛亦。 因此,你總可以通過自己維護(hù)或雇人維護(hù)來獲得更長期的支持仙蚜。 就目前來說此洲,release-1.0仍將繼續(xù)是一個優(yōu)秀的,穩(wěn)定的LTS分支委粉。 并且呜师,當(dāng)我們打算更替LTS分支時,我們會提前發(fā)布大量警告(warning)贾节。
不同的版本分支對應(yīng)于不同的風(fēng)險承受力
不同的用戶有不同的風(fēng)險承受力(risk tolerance)汁汗。 一些風(fēng)險承受力高的用戶能駕輕就熟地發(fā)現(xiàn)并匯報零星的bug,并偵察出為什么某個第三方庫與Julia的新版本不兼容栗涂。 另一些風(fēng)險承受力低的用戶希望使用久經(jīng)考驗(yàn)知牌,廣泛兼容的版本。 還有些用戶介于這兩個極端間的某處斤程。 大致可以把大多數(shù)用戶根據(jù)風(fēng)險承受力分為下面四類:
高風(fēng)險承受力(high risk tolerance):“人生只有一次角寸,我在master分支上翩翩起舞。況且忿墅,master分支在未來的相當(dāng)長一段時間內(nèi)不會有破壞兼容性的更新4”馀海現(xiàn)在master只偶爾出現(xiàn)bug,不過就算出現(xiàn)bug疚脐,我也可以幫忙解決亿柑。”
普通風(fēng)險承受力(normal risk tolerance):“我想要能用的東西棍弄,我不想要master分支上忽隱忽現(xiàn)的bug望薄。所以我會堅(jiān)守最新的穩(wěn)定版本并打上最新的補(bǔ)丁,這樣我的系統(tǒng)又安全又高效呼畸。惟一的煩惱是當(dāng)我使用的第三方庫因?yàn)橐蕾囂蕴腏ulia內(nèi)部代碼而在新版本上失靈時痕支,我需要等上一段時間第三方庫作者才會更新÷”
低風(fēng)險承受力(low risk tolerance):“我保守卧须,厭惡風(fēng)險。我使用當(dāng)前的LTS分支,因?yàn)樗呀?jīng)經(jīng)歷了充分的測試故慈。當(dāng)LTS分支更替時板熊,我將升級到新的LTS分支。因?yàn)樾碌腖TS分支在成為長期支持前已經(jīng)經(jīng)歷了數(shù)個補(bǔ)丁版本察绷,所以bug應(yīng)該已經(jīng)被修復(fù)干签,第三方庫不兼容問題應(yīng)該也已經(jīng)被解決〔鸷常”
極低風(fēng)險承受力(very low risk tolerance):“我極端厭惡風(fēng)險容劳。除了嚴(yán)重的bug和安全問題,我從不升級Julia(或其它任何東西)闸度。我運(yùn)行一個已經(jīng)不再被支持的LTS版本竭贩,但這個版本已經(jīng)經(jīng)歷了兩位數(shù)的補(bǔ)丁,相當(dāng)可靠莺禁。如果我需要修復(fù)一個新的bug留量,我將自己動手反向移植∮炊”
這些不同類型的需求很好地詮釋了LTS分支的關(guān)鍵特性:
它被充分地打上補(bǔ)丁楼熄,非常可靠浩峡;
每個想要支持它的第三方庫都已經(jīng)發(fā)布了支持它的庫版本可岂。
如果一個新的LTS分支滿足這兩個條件,低風(fēng)險承受力用戶便會升級到該版本翰灾,因?yàn)樗麄兿嘈旁撔翷TS分支可靠缕粹,經(jīng)過充分調(diào)試,并且需要的第三方庫已經(jīng)向其提供支持(可能需要同時更新庫版本)纸淮。 我們將從實(shí)踐中學(xué)習(xí)新的LTS分支在獨(dú)當(dāng)一面前需要滯后穩(wěn)定分支多少版本平斩。
發(fā)布流程
我們已經(jīng)討論了各種版本以及它們所允許的修改,但我們還沒深入討論這些版本的發(fā)布流程萎馅。 在這一節(jié)里双戳,我將描繪這些細(xì)節(jié)虹蒋,諸如從master上的新特性到次要版本的發(fā)布糜芳,再到為次要版本發(fā)布補(bǔ)丁。 在這節(jié)里魄衅,“bug”一詞不僅指代傳統(tǒng)意義上的錯誤代碼峭竣,也同時指代性能問題(運(yùn)行效率不可接受之低的代碼)。 在Julia語言中晃虫,性能至關(guān)重要皆撩,我們經(jīng)常將性能問題視為不可繞過的bug。 以下是一連串圍繞x.y.0次要版本展開的各階段與標(biāo)志性事件:
開發(fā)(development),4個月
在master分支上
開發(fā)新特性扛吞,修復(fù)bug等等呻惕。
標(biāo)記x.y.0-alpha(非強(qiáng)制)
新版本的最早預(yù)覽——尚未特性凍結(jié)并可能存在已知bug
標(biāo)記x.y.0-beta(非強(qiáng)制)
新版本的稍后預(yù)覽——仍未特性凍結(jié)并可能存在已知bug
x.y.0特性凍結(jié)
創(chuàng)立release-x.y這個不穩(wěn)定版本分支
不接受新特性,只接受bug修復(fù)
新特性會被歸并到master分支滥比,而不是x.y.z分支
穩(wěn)定化(stabilization)亚脆,1-4個月
在release-x.y分支上
修復(fù)所有已知的阻礙發(fā)布的bug
標(biāo)記x.y.0-rc1
修復(fù)所有已知的阻礙發(fā)布的bug
標(biāo)記x.y.0-rc2
修復(fù)所有已知的阻礙發(fā)布的bug
…
標(biāo)記x.y.0-rcN
一周內(nèi)沒有出現(xiàn)阻礙發(fā)布的bug
標(biāo)記x.y.0
維護(hù)(maintenance),直到宣布x.y停止維護(hù)
在release-x.y分支上
向后移植bug修復(fù)到release-x.y分支上
標(biāo)記x.y.1(一到兩個月后)
向后移植bug修復(fù)到release-x.y分支上
…
一眼望去盲泛,你就能發(fā)現(xiàn)這是一條道阻且長的征途濒持。 尤其是穩(wěn)定化階段,它的費(fèi)時忽長忽短寺滚,從幾周到數(shù)月不等柑营,難以預(yù)料。 質(zhì)量至上的愿望和如期發(fā)布的憧憬相互沖突村视,如同一根兩頭尖的針官套。 一方面,我們不想還未調(diào)試好就匆忙發(fā)布蚁孔,以免因?yàn)榇种茷E造而叫人失望虏杰。 另一方面,我們不想因?yàn)樵谡{(diào)試上花費(fèi)超額時間而導(dǎo)致無法準(zhǔn)時地發(fā)布次要版本——盡管我們都知道軟件開發(fā)勒虾,尤其是復(fù)雜的程序語言開發(fā)纺阔,跳票是家常便飯的事。
為了解決這一矛盾修然,我們想了一個好主意笛钝。 如果我們同時開展一個版本的穩(wěn)定化和下一版本的開發(fā),我們就有望如期完成版本迭代愕宋。 每個次要版本的開發(fā)階段占用固定的四個月的時間玻靡,x.y版本的開發(fā)階段一結(jié)束,x.(y+1)版本的開發(fā)階段就立即開始中贝。 雷打不動地囤捻,我們每四個月進(jìn)行一次特性凍結(jié):一旦我們決定了特性凍結(jié)的日子,你要么加把勁在這之前匯入(merge)你開發(fā)的特性邻寿,要么索性等下一個版本蝎土。 這個操作方法也意味著master分支永遠(yuǎn)開放用于接受新特性,而不會像不穩(wěn)定分支那樣在穩(wěn)定化階段凍結(jié)绣否。
由于開發(fā)與穩(wěn)定化的時間重疊誊涯,如果版本候選過程耗時過長,很有可能x.y.0的最終版本將在x.(y+1).0特性凍結(jié)時發(fā)布蒜撮。 一個最好的例子便是1.2.0版本和1.3.0版本暴构。 雖然這在discourse上引起了一些困惑和驚愕,但這種副作用是維持可預(yù)測發(fā)布周期所必要的。?1.2版本的穩(wěn)定化階段不尋常的長取逾,但這并沒有什么好奇怪的耗绿。 我們時時檢視我們的開發(fā)流程,反思如何改進(jìn)砾隅。 一個可能的改進(jìn)是更頻繁地調(diào)用PkgEval以及自動化這一過程缭乘。 這樣我們就能盡早地知道何時我們破壞了與第三方庫的兼容性。 調(diào)用PkgEval越早琉用,調(diào)用PkgEval越頻繁堕绩,我們也就越容易鎖定破壞兼容性的變動。 如果有人愿意幫助改善Julia的發(fā)布流程邑时,一個行之有效的途徑就是替我們多多調(diào)用PkgEval奴紧,而且這不需要什么高深的技術(shù)知識。
有一點(diǎn)需要注意晶丘,特性凍結(jié)只凍結(jié)了特性黍氮,不凍結(jié)bug修復(fù)。 Bug修復(fù)在任何時間在任何分支上都是允許的浅浮。 修復(fù)bug永遠(yuǎn)不會遲沫浆。 只有一種情況bug修復(fù)不進(jìn)入版本分支,那就是該分支已被遺棄了滚秩。 即便如此专执,如果有人愿意修復(fù)遺棄分支的bug并發(fā)布一個新版本,我們舉雙手歡迎郁油,只不過我們不自己帶頭罷了本股。
要預(yù)發(fā)布版本有何用?
雖然預(yù)發(fā)布版本(pre-release)是版本發(fā)布流程的標(biāo)準(zhǔn)組成部分桐腌,并不是所有人都對alpha和beta版本乃至候選版本(release candidate)的意義了若指掌拄显。 這些預(yù)發(fā)布版本的意義何在? 我起初對此也懵懵懂懂案站,直到我開始自己發(fā)布軟件版本躬审。 這些預(yù)發(fā)布版本其實(shí)是一種溝通,一種和所有依賴你的軟件的用戶的溝通蟆盐。 它們向你的用戶發(fā)出信號:“親承边,來試試看這個〔涨荩” 每一個預(yù)發(fā)布版本向各種用戶請求不同的反饋:
alpha版本說道:“我的特性還不齊全炒刁,而且?guī)缀跻欢ㄓ衎ug恩沽,但請給我一些關(guān)于這些重要新特性的反饋誊稚,以便于我在木已成舟之前做出適當(dāng)?shù)男薷摹!?/p>
beta版本和alpha版本很相似里伯,但更完善城瞎,含有較少的bug。我們只在0.6和0.7版本5發(fā)布過beta版本(兩者之前都已有alpha版本)疾瓮。
候選版本說道:“這下總算快完成了脖镀,請測試并告訴我們是否有bug。如果不這樣做狼电,我們發(fā)布的版本可能會含有影響你的應(yīng)用的bug蜒灰。”候選版本肩碟,只要不含阻礙發(fā)布的bug强窖,隨時都會成為下一個正式版本。
所以削祈,下次當(dāng)你看到一個預(yù)發(fā)布版本翅溺,不要錯過嘗試它的機(jī)會! 讓我們知道它是否為你正常工作髓抑。 如果你這樣做的話咙崎,最終版本就會給你帶來平滑,高質(zhì)量的使用體驗(yàn)吨拍。
版本維護(hù)
關(guān)于bug修復(fù)褪猛,一個(次要)版本的生命并不隨著其貼上x.y.0標(biāo)簽而結(jié)束。 后面一系列叫x.y.z的補(bǔ)丁版本正翹首以待呢羹饰。 這又是怎么一回事握爷? 所有活躍分支都需要修復(fù)bug,但bug修復(fù)通常進(jìn)行于最新的分支严里,隨后才反向移植到之前的活躍分支新啼。 譬如,master上有個bug刹碾,這個bug會以pull請求(pull request燥撞,PR)的方式被修復(fù)。 同時迷帜,這個bug每波及一個活躍分支物舒,該P(yáng)R就會被貼上相應(yīng)的backport x.y的GitHub標(biāo)簽(label)。 當(dāng)前的活躍分支為master戏锹,release-1.3(不穩(wěn)定)冠胯,release-1.2(穩(wěn)定),和release-1.0(LTS)锦针,這個PR會被貼上相應(yīng)的backport 1.3荠察,?backport 1.2置蜀,和backport 1.0標(biāo)簽。 這個代碼改動隨后通過甄選(使用git cherry-pick -x)運(yùn)用于這些分支中的每一個悉盆,并成為下個補(bǔ)丁版本的一部分盯荤。 如果修復(fù)成功,測試通過焕盟,則皆大歡喜秋秤。 如果失敗,則需通過額外的手工勞動修復(fù)這些分支上的bug脚翘。
一旦某個版本分支積累了足夠的bug修復(fù)灼卢,并且經(jīng)歷了足夠的時間,一個新的補(bǔ)丁版本x.y.z就誕生了来农。 相關(guān)消息會在discourse上提前五天公布芥玉,以便于用戶測試新版本。 我們目前沒有精力或資源為補(bǔ)丁版本制作二進(jìn)制程序體(binary)或候選版本——它們多如牛毛备图。 因此灿巧,你要么使用一個每日構(gòu)建(nightly build),要么自己從源碼編譯揽涮。 如果你想助我們一臂之力抠藕,自動化并精簡補(bǔ)丁版本發(fā)布流程是另一個高影響力的工作6。
結(jié)論
但愿你讀完這篇關(guān)于Julia版本發(fā)布流程和政策的綜述后有所啟迪蒋困。 我們最想看到的是你們當(dāng)中的某些人讀完之后參與到Julia的事業(yè)中盾似,同時也希望通過揭秘Julia的發(fā)布流程,我們降低了成為Julia開發(fā)人員的門檻雪标。
PkgEval工具能運(yùn)行所有Julia第三方庫(package)的測試套件零院。它確保了我們不會不經(jīng)意間造成不兼容問題。一旦發(fā)現(xiàn)不兼容村刨,我們一方面會檢查我們的版本是否違背了SemVer告抄,另一方面(無論不兼容的責(zé)任方是誰)向該第三方庫發(fā)送pull請求(pull request,PR)嵌牺。?
譯者注:release-1.3是此文寫作時的版本分支打洼。?
譯者注:2.0為此文寫作時的未來主要版本。?
譯者注:根據(jù)前文逆粹,破壞兼容性的更新在開發(fā)2.0版本時才會出現(xiàn)募疮。?
0.7即為包含棄用(deprecation)的1.0版本。?
譯者注:前一個高影響力的工作是幫助調(diào)用PkgEval僻弹。?
07 Sep 2019?|?Stefan Karpinski (Julia Computing)