如何从源代码安装软件…然后将其删除

时间:2020-01-09 10:41:00  来源:igfitidea点击:

简介:本详细指南说明了如何在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

现在,如果使用软件包管理器在Node.js或者Ubuntu上安装NodeJ变得非常简单。但是,让我们通过源代码进行操作。

步骤1:从GitHub获取源代码

像许多开源项目一样,NodeJS的源代码可以在GitHub上找到:https://github.com/nodejs/node

所以,我们直接去那里。

如果我们不熟悉GitHub,git或者值得一提的任何其他版本控制系统,则该存储库包含该软件的当前源以及该软件历年以来所做的所有修改的历史。最终直到为该项目编写的第一行。对于开发人员而言,保留该历史记录具有许多优势。对于我们今天来说,主要的是,我们将能够从项目的任何特定时间点获得资源。更精确地讲,我将能够获得我想要的8.1.1版本发布时的源。即使此后进行了许多修改。

在GitHub上,我们可以使用"分支"按钮在软件的不同版本之间导航。 "分支"和"标签"在Git中是一些相关的概念。基本上,开发人员创建"分支"和"标签"以跟踪项目历史中的重要事件,例如何时开始使用新功能或者何时发布发行版。我不会在这里详细介绍,我们需要知道的是我正在寻找标记为" v8.1.1"的版本

在选择了" v8.1.1"标签之后,页面会刷新,最明显的变化是该标签现在作为URL的一部分出现。另外,我们会注意到文件更改日期也不同。我们现在看到的源树是创建v8.1.1标记时存在的源树。从某种意义上讲,我们可以将git之类的版本控制工具视为时间旅行机器,从而使我们可以来回浏览项目历史记录。

此时,我们可以下载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风格的构建系统通过使用两个工具configure和make来简化任务。

" configure"文件是一个特定于项目的脚本,它将检查目标系统配置和可用功能,以确保可以构建项目,并最终处理当前平台的特性。

典型的"配置"工作的重要部分是构建" Makefile"。该文件包含有效构建项目所需的说明。

另一方面,make工具是POSIX工具,可在任何类Unix系统上使用。它将读取特定于项目的" Makefile",并执行所需的操作以构建和安装程序。

但是,像在Linux世界中一样,我们仍然可以宽容定制特定需求的构建。

./configure --help

configure -help命令将显示所有可用的配置选项。再次,这是非常特定于项目的。老实说,有时有必要在完全理解每个配置选项的含义之前深入研究项目。

但是我们必须至少知道一个标准的GNU Autotools选项:--prefix选项。这与文件系统层次结构以及软件的安装位置有关。

步骤3:FHS

典型发行版上的Linux文件系统层次结构主要符合文件系统层次结构标准(FHS)

该标准解释了系统各个目录的用途:/ usr,/ tmp,/ var等。

当使用GNU Autotools以及大多数其他构建系统时,新软件的默认安装位置为/ usr / local。根据FSH的说法,哪个是一个不错的选择," / usr / local层次结构供系统管理员在本地安装软件时使用?更新系统软件时,必须确保它不会被覆盖。它可以用于可在一组主机之间共享但在/ usr中找不到的程序和数据。"

/ usr / local层次结构以某种方式复制了根目录,我们会在其中找到可执行程序的/ usr / local / bin,对于库的/ usr / local /可以找到/ usr / local / lib。用于与架构无关的文件共享等等。

使用/ usr / local树进行自定义软件安装时,唯一的问题是所有软件的文件都将在其中混合在一起。尤其是,在安装了两个软件之后,将很难跟踪/ usr / local / bin和/ usr / local / lib到底属于哪个软件。但这不会对系统造成任何问题。毕竟,/ usr / bin几乎是一团糟。但这将成为我们要删除手动安装的软件的问题。

为了解决这个问题,我通常更喜欢在/ opt子树中安装自定义软件。再次引用FHS:

_" / opt保留用于安装添加应用程序软件包。

要安装在/ opt中的软件包,必须将其静态文件放在单独的/ opt / <package>或者/ opt / <provider>目录树中,其中<package>是描述软件包的名称,而<provider>是提供商的LANANA注册名称。" _

因此,我们将专门为自定义NodeJS安装创建一个/ opt子目录。如果有一天我想删除该软件,则只需删除该目录:

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"开始

theitroad@localhost:~$ git clone --depth 1 \
                             --branch v8.1.1 \
                             https:&#47;&#47;github.com/nodejs/node
-bash: git: command not found

这个问题很容易诊断和解决。只需安装git软件包:

theitroad@localhost:~$ sudo apt-get install git
theitroad@localhost:~$ git clone --depth 1 \
                             --branch v8.1.1 \
                             https://github.com/nodejs/node && echo ok
&#091;...]
ok
theitroad@localhost:~/node$ sudo mkdir /opt/node-v8.1.1
theitroad@localhost:~/node$ sudo ln -sT node-v8.1.1 /opt/node

没问题

theitroad@localhost:~/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.

显然,要编译项目,我们需要一个编译器。使用C ++语言编写的NodeJS,我们需要一个C ++编译器。在这里,我将为此安装GNU C ++编译器" g ++":

theitroad@localhost:~/node$ sudo apt-get install g++
theitroad@localhost:~/node$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
&#091;...]
ok
theitroad@localhost:~/node$ make -j9 && echo ok
-bash: make: command not found

另一个缺少的工具。相同的症状。相同的解决方案:

theitroad@localhost:~/node$ sudo apt-get install make
theitroad@localhost:~/node$ make -j9 && echo ok
&#091;...]
ok
theitroad@localhost:~/node$ sudo make install
&#091;...]
theitroad@localhost:~/node$ /opt/node/bin/node --version
v8.1.1

成功!

请注意:我已逐一安装了各种工具,以展示如何诊断编译问题并向我们展示解决这些问题的典型解决方案。但是,如果我们搜索有关该主题的更多信息或者阅读其他教程,我们会发现大多数发行版都具有"元软件包",作为安装某些或者所有用于编译软件的典型工具的保护伞。在基于Debian的系统上,我们可能会为此目的遇到build-essentials软件包。在基于Red-Hat的发行版上,这将是"开发工具"组。

从CentOS 7.0

&#091;theitroad@localhost ~]$ git clone --depth 1 \
                               --branch v8.1.1 \
                               https:&#47;&#47;github.com/nodejs/node
-bash: git: command not found

找不到相关命令?只需使用yum软件包管理器进行安装:

&#091;theitroad@localhost ~]$ sudo yum install git
&#091;theitroad@localhost ~]$ git clone --depth 1 \
                               --branch v8.1.1 \
                               https://github.com/nodejs/node && echo ok
&#091;...]
ok
&#091;theitroad@localhost ~]$ sudo mkdir /opt/node-v8.1.1
&#091;theitroad@localhost ~]$ sudo ln -sT node-v8.1.1 /opt/node
&#091;theitroad@localhost ~]$ cd node
&#091;theitroad@localhost 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 ++语言编写的,但是我的系统缺少相应的编译器。百胜救人。由于我不是CentOS的常规用户,因此实际上我必须在Internet上搜索包含g ++编译器的软件包的确切名称。带我到该页面:https://superuser.com/questions/590808/yum-install-gcc-g-doesnt-work-anymore-in-centos-6-4

&#091;theitroad@localhost node]$ sudo yum install gcc-c++
&#091;theitroad@localhost node]$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
&#091;...]
ok
&#091;theitroad@localhost node]$ make -j9 && echo ok
&#091;...]
ok
&#091;theitroad@localhost node]$ sudo make install && echo ok
&#091;...]
ok
&#091;theitroad@localhost node]$ /opt/node/bin/node --version
v8.1.1

成功。再次。

C.对从源代码安装的软件进行更改

我们可能需要从源代码安装软件,因为我们需要发行存储库中没有的非常特定的版本,或者因为我们想要修改程序以修复错误或者添加功能。毕竟,开源就是进行修改。因此,我将借此机会向我们介绍我们现在能够编译自己的软件所具有的强大功能。

在这里,我们将对NodeJS的来源进行较小的更改。我们将查看我们所做的更改是否将合并到软件的编译版本中:

在我们喜欢的文本编辑器(vim,nano,gedit等)中打开文件node / src / node.cc。并尝试找到该代码片段:

if (debug_options.ParseOption(argv&#091;0], arg)) {
      // Done, consumed by DebugOptions::ParseOption().
    } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
      printf("%s\n", 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&#091;0], arg)) {
       // Done, consumed by DebugOptions::ParseOption().
     } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
-      printf("%s\n", 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
&#091;...]
ok

这一次,它可能失败的唯一原因是我们在更改代码时输入了错误。如果是这种情况,请在文本编辑器中重新打开node / src / node.cc文件并解决错误。

设法编译并安装了新的修改后的NodeJS版本之后,我们将可以检查修改是否实际上已合并到软件中:

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

恭喜你!我们已对开源程序进行了首次更改!

D.让外壳找到我们的自定义构建软件

我们可能已经注意到,我总是通过指定二进制文件的绝对路径来启动新编译的NodeJS软件。

/opt/node/bin/node

有用。但这至少可以令人讨厌。实际上,有两种常见的解决方法。

实际上,有两种常见的方法可以解决烦人的问题,即指定二进制文件的绝对路径,
但是要了解它们,我们必须首先知道外壳程序仅通过在PATH环境变量指定的目录中查找可执行文件来找到它们。

theitroad@localhost:~/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将报告错误"命令"未找到"。

鉴于此,我们提供了两种方法使外壳程序可以访问命令:将其添加到已配置的PATH目录之一中。或者通过将包含我们的可执行文件的目录添加到" PATH"。

从/ usr / local / bin添加链接

仅将节点二进制可执行文件从" / opt / node / bin"复制到" / usr / local / bin"是一个坏主意,因为这样做,可执行程序将不再能够找到属于该文件的其他必需组件。 / opt / node /(软件相对于其自身位置定位资源文件是一种常见的做法)。

因此,传统的方式是使用符号链接:

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

这是一种简单有效的解决方案,尤其是在软件包仅由少数几个知名的可执行程序组成的情况下,因为我们必须为每个用户可调用的命令创建一个符号链接。例如,如果我们熟悉NodeJS,我们就会知道npm配套应用程序,我也应该从/ usr / local / bin中进行符号链接。但是,我将此作为练习。

修改PATH

首先,如果尝试了上述解决方案,请删除先前创建的节点符号链接,以从清除状态开始:

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

现在,这是更改PATH的魔术命令:

theitroad@localhost:~/node$ export PATH="/opt/node/bin:${PATH}"
theitroad@localhost:~/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命令确认:

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

在我们将符号链接创建到/ usr / local / bin中之后,"链接"解决方案便是永久的,而对PATH的更改仅在当前shell中有效。我将留给我们一些研究,以了解如何更改" PATH"永久物。提示,这与"个人资料"有关。如果我们找到解决方案,请通过下面的评论部分与其他读者分享!

E.如何从源代码中删除该新安装的软件

由于我们定制的编译NodeJS软件完全位于/opt/node-v8.1.1目录中,因此与使用rm命令删除该目录相比,删除该软件不需要付出更多的努力:

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

注意:sudo和rm -rf是危险的鸡尾酒!在按" enter"键之前,请务必检查两次命令。如果我们删除错误的目录,则不会有任何确认消息,也不会取消删除…

然后,如果我们修改了" PATH",则必须还原这些更改,这一点都不复杂。

而且,如果我们是从/ usr / local / bin创建的链接,则必须将其全部删除:

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