Linux中國

詳解如何用源代碼安裝軟體,以及如何卸載它

簡介:這篇文章詳細介紹了在 Linux 中怎麼用源代碼安裝程序,以及怎麼去卸載用源代碼安裝的程序。

Linux 發行版的一個最大的優點就是它的包管理器和相關的軟體庫。通過它們提供的資源和工具,你才能夠以完全自動化的方式在你的計算機上下載和安裝軟體。

但是,儘管付出了很多的努力,包維護者仍然沒法照顧好每種情況,也不可能將所有的可用軟體都打包進去。因此,仍然存在需要你自已去編譯和安裝一個新軟體的情形。對於我來說,到目前為止,最主要的原因是,我編譯一些軟體是我需要去運行一個特定的版本。或者是我想去修改源代碼或使用一些想要的編譯選項。

如果你也屬於後一種情況,那你已經知道你應該怎麼做了。但是,對於絕大多數的 Linux 用戶來說,第一次從源代碼中編譯和安裝一個軟體看上去像是一個入門儀式:它讓很多人感到恐懼;但是,如果你能克服困難,你將可能進入一個全新的世界,並且,如果你做到了,那麼你將成為社區中享有特權的一部分人。

A. 在 Linux 中從源代碼開始安裝軟體

這正是我們要做的。因為這篇文章的需要,我要在我的系統上安裝 NodeJS 8.1.1。它是個完全真實的版本。這個版本在 Debian 倉庫中沒有:

sh$ apt-cache madison nodejs | grep amd64
    nodejs | 6.11.1~dfsg-1 | http://deb.debian.org/debian experimental/main amd64 Packages
    nodejs | 4.8.2~dfsg-1 | http://ftp.fr.debian.org/debian stretch/main amd64 Packages
    nodejs | 4.8.2~dfsg-1~bpo8+1 | http://ftp.fr.debian.org/debian jessie-backports/main amd64 Packages
    nodejs | 0.10.29~dfsg-2 | http://ftp.fr.debian.org/debian jessie/main amd64 Packages
    nodejs | 0.10.29~dfsg-1~bpo70+1 | http://ftp.fr.debian.org/debian wheezy-backports/main amd64 Packages

第 1 步:從 GitHub 上獲取源代碼

像大多數開源項目一樣,NodeJS 的源代碼可以在 GitHub:https://github.com/nodejs/node 上找到。

所以,我們直接開始吧。

The NodeJS official GitHub repository

如果你不熟悉 GitHubgit 或者提到的其它 版本管理系統包含了這個軟體的源代碼,以及多年來對該軟體的所有修改的歷史。甚至可以回溯到該軟體的最早版本。對於開發者來說,保留它的歷史版本有很多好處。如今對我來說,其中一個好處是可以得到任何一個給定時間點的項目源代碼。更準確地說,我可以得到我所要的 8.1.1 發布時的源代碼。即便從那之後他們有了很多的修改。

Choose the v8.1.1 tag in the NodeJS GitHub repository

在 GitHub 上,你可以使用 「branch」 (分支)按鈕導航到這個軟體的不同版本。「分支」 和 「標籤」 是 Git 中一些相關的概念。總的來說,開發者創建 「分支」 和 「標籤」 來在項目歷史中對重要事件保持跟蹤,比如當他們啟用一個新特性或者發布一個新版本時。在這裡先不詳細介紹了,你現在只需要知道我在找被標記為 「v8.1.1」 的版本。

The NodeJS GitHub repository as it was at the time the v8.1.1 tag was created

在選擇了 「v8.1.1」 標籤後,頁面被刷新,最顯著的變化是標籤現在作為 URL 的一部分出現。另外,你可能會注意到文件改變日期也有所不同。你現在看到的源代碼樹是創建了 v8.1.1 標籤時的代碼。在某種意義上,你也可以認為像 git 這樣的版本管理工具是一個時光穿梭機,允許你在項目歷史中來回穿梭。

NodeJS GitHub repository download as a ZIP button

此時,我們可以下載 NodeJS 8.1.1 的源代碼。你不要忘記去點那個建議的大的藍色按鈕來下載一個項目的 ZIP 壓縮包。對於我來說,為講解的目的,我從命令行中下載並解壓這個 ZIP 壓縮包。但是,如果你更喜歡使用一個 GUI 工具,不用擔心,你可以取代下面的命令方式:

wget https://github.com/nodejs/node/archive/v8.1.1.zip
unzip v8.1.1.zip
cd node-8.1.1/

下載一個 ZIP 包就可以,但是如果你希望「像個專家一樣」,我建議你直接使用 git 工具去下載源代碼。它一點也不複雜 — 並且如果你是第一次使用該工具,它將是一個很好的開端,你以後將經常用到它:

# first ensure git is installed on your system
sh$ sudo apt-get install git
# Make a shallow clone the NodeJS repository at v8.1.1
sh$ git clone --depth 1 
              --branch v8.1.1 
              https://github.com/nodejs/node
sh$ cd node/

順便說一下,如果你有任何問題,這篇文章的第一部分只是做一個總體介紹而已。後面,為了幫你排除常見問題,我們將基於 Debian 和基於 RedHat 的發行版更詳細地解釋。

不管怎樣,在你使用 git 或者作為一個 ZIP 壓縮包下載了源代碼後,在當前目錄下就有了同樣的源代碼文件:

sh$ ls
android-configure  BUILDING.md            common.gypi      doc            Makefile   src
AUTHORS            CHANGELOG.md           configure        GOVERNANCE.md  node.gyp   test
benchmark          CODE_OF_CONDUCT.md     CONTRIBUTING.md  lib            node.gypi  tools
BSDmakefile        COLLABORATOR_GUIDE.md  deps             LICENSE        README.md  vcbuild.bat

第 2 步:理解程序的構建系統

構建系統就是我們通常所說的「編譯源代碼」,其實,編譯只是從源代碼中生成一個可使用的軟體的其中一個階段。構建系統是一套工具,用於自動處置不同的任務,以便可以僅通過幾個命令就能構建整個軟體。

雖然概念很簡單,實際上編譯做了很多事情。因為不同的項目或者編程語言也許有不同的要求,或者因為編程者的好惡,或者因為支持的平台、或者因為歷史的原因,等等等等 … 選擇或創建另外一個構建系統的原因幾乎數不清。這方面有許多種不同的解決方案。

NodeJS 使用一種 GNU 風格的構建系統。這在開源社區中這是一個很流行的選擇。由此開始,你將進入一段精彩的旅程。

寫出和調優一個構建系統是一個非常複雜的任務。但是,作為 「終端用戶」 來說,GNU 風格的構建系統使用兩個工具讓他們免於此難:configuremake

configure 文件是個項目專用的腳本,它將檢查目標系統的配置和可用功能,以確保該項目可以被構建,並最終吻合當前平台的特性。

一個典型的 configure 任務的重要部分是去構建 Makefile。這個文件包含了有效構建項目所需的指令。

另一方面,make 工具,這是一個可用於任何類 Unix 系統的 POSIX 工具。它將讀取項目專用的 Makefile 然後執行所需的操作去構建和安裝你的程序。

但是,在 Linux 的世界中,你仍然有一些定製你自己專用的構建的理由。

./configure --help

configure -help 命令將展示你可用的所有配置選項。再強調一下,這是非常的項目專用。說實話,有時候,在你完全理解每個配置選項的作用之前,你需要深入到項目中去好好研究。

不過,這裡至少有一個標準的 GNU 自動化工具選項是你該知道的,它就是眾所周知的 --prefix 選項。它與文件系統的層次結構有關,它是你軟體要安裝的位置。

第 3 步:文件系統層次化標準(FHS)

大部分典型的 Linux 發行版的文件系統層次結構都遵從 文件系統層次化標準(FHS)

這個標準說明了你的系統中各種目錄的用途,比如,/usr/tmp/var 等等。

當使用 GNU 自動化工具 和大多數其它的構建系統 時,它會把新軟體默認安裝在你的系統的 /usr/local 目錄中。這是依據 FHS 中 /usr/local 層級是為系統管理員本地安裝軟體時使用的,它在系統軟體更新覆蓋時是安全的。它也可以用於存放在一組主機中共享,但又沒有放到 /usr 中的程序和數據」,因此,它是一個非常好的選擇。

/usr/local 層級以某種方式複製了根目錄,你可以在 /usr/local/bin 這裡找到可執行程序,在 /usr/local/lib 中找到庫,在 /usr/local/share 中找到架構無關的文件,等等。

使用 /usr/local 樹作為你定製安裝的軟體位置的唯一問題是,你的軟體的文件將在這裡混雜在一起。尤其是你安裝了多個軟體之後,將很難去準確地跟蹤 /usr/local/bin/usr/local/lib 中的哪個文件到底屬於哪個軟體。它雖然不會導致系統的問題。畢竟,/usr/bin 也是一樣混亂的。但是,有一天你想去卸載一個手工安裝的軟體時它會將成為一個問題。

要解決這個問題,我通常喜歡安裝定製的軟體到 /opt 子目錄下。再次引用 FHS:

/opt 是為安裝附加的應用程序軟體包而保留的。

包安裝在 /opt 下的軟體包必須將它的靜態文件放在單獨的 /opt/<package> 或者 /opt/<provider> 目錄中,此處 <package> 是所說的那個軟體名的名字,而 <provider> 處是提供者的 LANANA 註冊名字。」(LCTT 譯註:LANANA 是指 The Linux Assigned Names And Numbers Authority。 )

因此,我們將在 /opt 下創建一個子目錄,用於我們定製的 NodeJS 安裝。並且,如果有一天我想去卸載它,我只是很簡單地去刪除那個目錄:

sh$ sudo mkdir /opt/node-v8.1.1
sh$ sudo ln -sT node-v8.1.1 /opt/node
# What is the purpose of the symbolic link above?
# Read the article till the end--then try to answer that
# question in the comment section!

sh$ ./configure --prefix=/opt/node-v8.1.1
sh$ make -j9 && echo ok
# -j9 means run up to 9 parallel tasks to build the software.
# As a rule of thumb, use -j(N+1) where N is the number of cores
# of your system. That will maximize the CPU usage (one task per
# CPU thread/core + a provision of one extra task when a process
# is blocked by an I/O operation.

在你運行完成 make 命令之後,如果有任何的除了 「ok」 以外的信息,將意味著在構建過程中有錯誤。當我們使用一個 -j 選項去運行並行構建時,在構建系統的大量輸出過程中,檢索錯誤信息並不是件很容易的事。

在這種情況下,只能是重新開始 make,並且不要使用 -j 選項。這樣錯誤將會出現在輸出信息的最後面:

sh$ make

最終,編譯結束後,你可以運行這個命令去安裝你的軟體:

sh$ sudo make install

然後測試它:

sh$ /opt/node/bin/node --version
v8.1.1

B. 如果在源代碼安裝的過程中出現錯誤怎麼辦?

我上面介紹的大多是你能在文檔完備的項目的「構建指令」頁面上看到。但是,本文的目標是讓你從源代碼開始去編譯你的第一個軟體,它可能要花一些時間去研究一些常見的問題。因此,我將再次重新開始一遍整個過程,但是,這次是在一個最新的、最小化安裝的 Debian 9.0 和 CentOS 7.0 系統上。因此,你可能看到我遇到的錯誤以及我怎麼去解決它。

從 Debian 9.0 中 「Stretch」 開始

itsfoss@debian:~$ git clone --depth 1 
                             --branch v8.1.1 
                             https://github.com/nodejs/node
-bash: git: command not found

這個問題非常容易去診斷和解決。去安裝這個 git 包即可:

itsfoss@debian:~$ sudo apt-get install git
itsfoss@debian:~$ git clone --depth 1 
                             --branch v8.1.1 
                             https://github.com/nodejs/node && echo ok
[...]
ok
itsfoss@debian:~/node$ sudo mkdir /opt/node-v8.1.1
itsfoss@debian:~/node$ sudo ln -sT node-v8.1.1 /opt/node

現在沒有問題了。

itsfoss@debian:~/node$ ./configure --prefix=/opt/node-v8.1.1/
WARNING: failed to autodetect C++ compiler version (CXX=g++)
WARNING: failed to autodetect C compiler version (CC=gcc)
Node.js configure error: No acceptable C compiler found!
        Please make sure you have a C compiler installed on your system and/or
        consider adjusting the CC environment variable if you installed
        it in a non-standard prefix.

很顯然,編譯一個項目,你需要一個編譯器。NodeJS 是使用 C++ 語言 寫的,我們需要一個 C++ 編譯器。在這裡我將安裝 g++,它就是為這個目的寫的 GNU C++ 編譯器:

itsfoss@debian:~/node$ sudo apt-get install g++
itsfoss@debian:~/node$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
[...]
ok
itsfoss@debian:~/node$ make -j9 && echo ok
-bash: make: command not found

還差一個其它工具。同樣的癥狀。同樣的解決方案:

itsfoss@debian:~/node$ sudo apt-get install make
itsfoss@debian:~/node$ make -j9 && echo ok
[...]
ok
itsfoss@debian:~/node$ sudo make install
[...]
itsfoss@debian:~/node$ /opt/node/bin/node --version
v8.1.1

成功!

請注意:我將一次又一次地安裝各種工具去展示怎麼去診斷編譯問題,以及展示怎麼去解決這些問題。但是,如果你搜索關於這個主題的更多文檔,或者讀其它的教程,你將發現,很多發行版有一個 「meta-packages」,它包羅了安裝一些或者全部的用於編譯軟體的常用工具。在基於 Debian 的系統上,你或許遇到過 build-essentials 包,它就是這種用作。在基於 Red Hat 的發行版中,它將是 「Development Tools」 組。

在 CentOS 7.0 上

[itsfoss@centos ~]$ git clone --depth 1 
                               --branch v8.1.1 
                               https://github.com/nodejs/node
-bash: git: command not found

命令沒有找到?可以用 yum 包管理器去安裝它:

[itsfoss@centos ~]$ sudo yum install git
[itsfoss@centos ~]$ git clone --depth 1 
                               --branch v8.1.1 
                               https://github.com/nodejs/node && echo ok
[...]
ok
[itsfoss@centos ~]$ sudo mkdir /opt/node-v8.1.1
[itsfoss@centos ~]$ sudo ln -sT node-v8.1.1 /opt/node
[itsfoss@centos ~]$ cd node
[itsfoss@centos node]$ ./configure --prefix=/opt/node-v8.1.1/
WARNING: failed to autodetect C++ compiler version (CXX=g++)
WARNING: failed to autodetect C compiler version (CC=gcc)
Node.js configure error: No acceptable C compiler found!

        Please make sure you have a C compiler installed on your system and/or
        consider adjusting the CC environment variable if you installed
        it in a non-standard prefix.

你知道的:NodeJS 是使用 C++ 語言寫的,但是,我的系統缺少合適的編譯器。Yum 可以幫到你。因為,我不是一個合格的 CentOS 用戶,我實際上是在互聯網上搜索到包含 g++ 編譯器的包的確切名字的。這個頁面指導了我:https://superuser.com/questions/590808/yum-install-gcc-g-doesnt-work-anymore-in-centos-6-4

[itsfoss@centos node]$ sudo yum install gcc-c++
[itsfoss@centos node]$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
[...]
ok
[itsfoss@centos node]$ make -j9 && echo ok
[...]
ok
[itsfoss@centos node]$ sudo make install && echo ok
[...]
ok
[itsfoss@centos node]$ /opt/node/bin/node --version
v8.1.1

再次成功!

C. 從源代碼中對要安裝的軟體做一些改變

從源代碼中安裝一個軟體,可能是因為你的分發倉庫中沒有一個可用的特定版本。或者因為你想去 修改 那個程序。也可能是修復一個 bug 或者增加一個特性。畢竟,開源軟體這些都可以做到。因此,我將抓住這個機會,讓你親自體驗怎麼去編譯你自己的軟體。

在這裡,我將在 NodeJS 源代碼上做一個微小改變。然後,我們將看到我們的改變將被納入到軟體的編譯版本中:

用你喜歡的 文本編輯器(如,vim、nano、gedit、 … )打開文件 node/src/node.cc。然後,嘗試找到如下的代碼片段:

   if (debug_options.ParseOption(argv[0], arg)) {
      // Done, consumed by DebugOptions::ParseOption().
    } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
      printf("%sn", NODE_VERSION);
      exit(0);
    } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
      PrintHelp();
      exit(0);
    }

它在 文件的 3830 行 附近。然後,修改包含 printf 的行,將它替換成如下內容:

      printf("%s (compiled by myself)n", NODE_VERSION);

然後,返回到你的終端。在繼續之前,為了對強大的 Git 支持有更多的了解,你可以去檢查一下,你修改是文件是否正確:

diff --git a/src/node.cc b/src/node.cc
index bbce1022..a5618b57 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -3828,7 +3828,7 @@ static void ParseArgs(int* argc,
     if (debug_options.ParseOption(argv[0], arg)) {
       // Done, consumed by DebugOptions::ParseOption().
     } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
-      printf("%sn", NODE_VERSION);
+      printf("%s (compiled by myself)n", NODE_VERSION);
       exit(0);
     } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
       PrintHelp();

在你前面改變的那行之前,你將看到一個 「-」 (減號標誌)。而在改變之後的行前面有一個 「+」 (加號標誌)。

現在可以去重新編譯並重新安裝你的軟體了:

make -j9 && sudo make install && echo ok
[...]
ok

這個時候,可能失敗的唯一原因就是你改變代碼時的輸入錯誤。如果就是這種情況,在文本編輯器中重新打開 node/src/node.cc 文件並修復錯誤。

一旦你完成了新修改版本的 NodeJS 的編譯和安裝,就可以去檢查你的修改是否包含到軟體中:

itsfoss@debian:~/node$ /opt/node/bin/node --version
v8.1.1 (compiled by myself)

恭喜你!你對開源程序做出了你的第一個改變!

D. 讓 shell 找到我們定製構建的軟體

到目前為止,你可能注意到,我通常啟動我新編譯的 NodeJS 軟體是通過指定到該二進位文件的絕對路徑。

/opt/node/bin/node

這是可以正常工作的。但是,這樣太麻煩。實際上有兩種辦法可以去解決這個問題。但是,去理解它們,你必須首先明白,你的 shell 定位可執行文件是通過在環境變數 PATH 中指定的目錄裡面查找的。

itsfoss@debian:~/node$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

在這個 Debian 系統上,如果你不指定一個精確的目錄做為命令名字的一部分,shell 將首先在 /usr/local/bin 中查找可執行程序;如果沒有找到,然後進入 /usr/bin 中查找;如果沒有找到,然後進入 /bin查找;如果沒有找到,然後進入 /usr/local/games 查找;如果沒有找到,然後進入 /usr/games 查找;如果沒有找到,那麼,shell 將報告一個錯誤,「command not found」

由此,我們可以知道有兩種方法去確保命令可以被 shell 訪問到:將它(該二進位程序)增加到已經配置好的 PATH 目錄中,或者將包含可執行程序的目錄添加到 PATH 中。

從 /usr/local/bin 中添加一個鏈接

只是從 /opt/node/bin拷貝 NodeJS 二進位可執行文件到 /usr/local/bin 是一個錯誤的做法。因為,如果這麼做,該可執行程序將無法定位到在 /opt/node/ 中的需要的其它組件。(軟體以它自己的位置去定位它所需要的資源文件是常見的做法)

因此,傳統的做法是去使用一個符號鏈接:

itsfoss@debian:~/node$ sudo ln -sT /opt/node/bin/node /usr/local/bin/node
itsfoss@debian:~/node$ which -a node || echo not found
/usr/local/bin/node
itsfoss@debian:~/node$ node --version
v8.1.1 (compiled by myself)

這一個簡單而有效的解決辦法,尤其是,如果一個軟體包是由好幾個眾所周知的可執行程序組成的,因為,你將為每個用戶調用的命令創建一個符號鏈接。例如,如果你熟悉 NodeJS,你知道應用的 npm 組件,也應該從 /usr/local/bin 做個符號鏈接。我把這個留給你做練習。

修改 PATH

首先,如果你嘗試過前面的解決方案,請先移除前面創建的節點符號鏈接,去從一個乾淨的狀態開始:

itsfoss@debian:~/node$ sudo rm /usr/local/bin/node
itsfoss@debian:~/node$ which -a node || echo not found
not found

現在,這裡有一個改變你的 PATH 的魔法命令:

itsfoss@debian:~/node$ export PATH="/opt/node/bin:${PATH}"
itsfoss@debian:~/node$ echo $PATH
/opt/node/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

簡單說就是,我用環境變數 PATH 之前的內容前綴了一個 /opt/node/bin 替換了其原先的內容。因此,你可以想像一下,shell 將先進入到 /opt/node/bin 目錄中查找可執行程序。我們也可以使用 which 命令去確認一下:

itsfoss@debian:~/node$ which -a node || echo not found
/opt/node/bin/node
itsfoss@debian:~/node$ node --version
v8.1.1 (compiled by myself)

鑒於 「符號鏈接」 解決方案是永久的,只要創建到 /usr/local/bin 的符號鏈接就行了,而對 PATH 的改變僅影響到當前的 shell。你可以自己做一些研究,如何做到對 PATH 的永久改變。給你一個提示,可以將它寫到你的 「profile」 中。如果你找到這個解決方案,不要猶豫,通過下面的評論區共享給其它的讀者!

E. 怎麼去卸載剛才從源代碼中安裝的軟體

因為我們定製編譯的 NodeJS 軟體全部在 /opt/node-v8.1.1 目錄中,卸載它不需要做太多的工作,僅使用 rm 命令去刪除那個目錄即可:

sudo rm -rf /opt/node-v8.1.1

注意:sudorm -rf 是 「非常危險的雞尾酒」!一定要在按下回車鍵之前多檢查幾次你的命令。你不會得到任何的確認信息,並且如果你刪除了錯誤的目錄它是不可恢復的 …

然後,如果你修改了你的 PATH,你可以去恢復這些改變。它一點也不複雜。

如果你從 /usr/local/bin 創建了一個符號鏈接,你應該去刪除它們:

itsfoss@debian:~/node$ sudo find /usr/local/bin 
                                 -type l 
                                 -ilname "/opt/node/*" 
                                 -print -delete
/usr/local/bin/node

等等? 依賴地獄在哪裡?

作為最終的討論,如果你讀過有關的編譯定製軟體的文檔,你可能聽到關於 依賴地獄 dependency hell 的說法。那是在你能夠成功編譯一個軟體之前,對那種煩人情況的一個別名,你必須首先編譯一個前提條件所需要的庫,它又可能要求其它的庫,而這些庫有可能與你的系統上已經安裝的其它軟體不兼容。

發行版的軟體包維護者的部分工作,就是實際去地解決那些依賴地獄,確保你的系統上的各種軟體都使用了兼容的庫,並且按正確的順序去安裝。

在這篇文章中,我特意選擇了 NodeJS 去安裝,是因為它幾乎沒有依賴。我說 「幾乎」 是因為,實際上,它 依賴。但是,這些源代碼的依賴已經預置到項目的源倉庫中(在 node/deps 子目錄下),因此,在你動手編譯之前,你不用手動去下載和安裝它們。

如果你有興趣了解更多關於那個問題的知識和學習怎麼去處理它。請在下面的評論區告訴我,它將是更高級別的文章的好主題!

作者簡介:

充滿激情的工程師,職業是教師,我的目標是:熱心分享我所教的內容,並讓我的學生自己培養它們的技能。你也可以在我的網站上聯繫到我。

via: https://itsfoss.com/install-software-from-source-code/

作者:Sylvain Leroux 譯者:qhwdw 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國