标签 编译 下的文章

使用 GCC 在单一的构建机器上来为不同的 CPU 架构交叉编译二进制文件。

如果你是一个开发者,要创建二进制软件包,像一个 RPM、DEB、Flatpak 或 Snap 软件包,你不得不为各种不同的目标平台编译代码。典型的编译目标包括 32 位和 64 位的 x86 和 ARM。你可以在不同的物理或虚拟机器上完成你的构建,但这需要你为何几个系统。作为代替,你可以使用 GNU 编译器集合 (GCC) 来交叉编译,在单一的构建机器上为几个不同的 CPU 架构产生二进制文件。

假设你有一个想要交叉编译的简单的掷骰子游戏。在大多数系统上,以 C 语言来编写这个相对简单,出于给添加现实的复杂性的目的,我以 C++ 语言写这个示例,所以程序依赖于一些不在 C 语言中东西 (具体来说就是 iostream)。

#include <iostream>
#include <cstdlib>

using namespace std;

void lose (int c); 
void win (int c); 
void draw (); 

int main() { 
  int i; 
    do { 
      cout << "Pick a number between 1 and 20: \n"; 
      cin >> i; 
      int c = rand ( ) % 21; 
      if (i > 20) lose (c); 
      else if (i < c ) lose (c); 
      else if (i > c ) win (c); 
      else draw (); 
      } 
      while (1==1); 
      }

void lose (int c ) 
  { 
    cout << "You lose! Computer rolled " << c << "\n"; 
  }

void win (int c ) 
  { 
    cout << "You win!! Computer rolled " << c << "\n"; 
   }

void draw ( ) 
   { 
     cout << "What are the chances. You tied. Try again, I dare you! \n";
   }

在你的系统上使用 g++ 命令编译它:

$ g++ dice.cpp -o dice

然后,运行它来确认其工作:

$ ./dice
Pick a number between 1 and 20:
[...]

你可以使用 file 命令来查看你刚刚生产的二进制文件的类型:

$ file ./dice
dice: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
linked (uses shared libs), for GNU/Linux 5.1.15, not stripped

同样重要,使用 ldd 命令来查看它链接哪些库:

$ ldd dice
linux-vdso.so.1 =&gt; (0x00007ffe0d1dc000)
libstdc++.so.6 =&gt; /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(0x00007fce8410e000)
libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6
(0x00007fce83d4f000)
libm.so.6 =&gt; /lib/x86_64-linux-gnu/libm.so.6
(0x00007fce83a52000)
/lib64/ld-linux-x86-64.so.2 (0x00007fce84449000)
libgcc_s.so.1 =&gt; /lib/x86_64-linux-gnu/libgcc_s.so.1
(0x00007fce8383c000)

从这些测试中,你已经确认了两件事:你刚刚运行的二进制文件是 64 位的,并且它链接的是 64 位库。

这意味着,为实现 32 位交叉编译,你必需告诉 g++ 来:

  1. 产生一个 32 位二进制文件
  2. 链接 32 位库,而不是 64 位库

设置你的开发环境

为编译成 32 位二进制,你需要在你的系统上安装 32 位的库和头文件。如果你运行一个纯 64 位系统,那么,你没有 32 位的库或头文件,并且需要安装一个基础集合。最起码,你需要 C 和 C++ 库(glibclibstdc++)以及 GCC 库(libgcc)的 32 位版本。这些软件包的名称可能在每个发行版中不同。在 Slackware 系统上,一个纯 64 位的带有 32 位兼容的发行版,可以从 Alien BOB 提供的 multilib 软件包中获得。在 Fedora、CentOS 和 RHEL 系统上:

$ yum install libstdc++-*.i686
$ yum install glibc-*.i686
$ yum install libgcc.i686

不管你正在使用什么系统,你同样必须安装一些你工程使用的 32 位库。例如,如果你在你的工程中包含 yaml-cpp,那么,在编译工程前,你必需安装 yaml-cpp 的 32 位版本,或者,在很多系统上,安装 yaml-cpp 的开发软件包(例如,在 Fedora 系统上的 yaml-cpp-devel)。

一旦这些处理好了,编译是相当简单的:

$ g++ -m32 dice.cpp -o dice32 -L /usr/lib -march=i686

-m32 标志告诉 GCC 以 32 位模式编译。-march=i686 选项进一步定义来使用哪种最优化类型(参考 info gcc 了解选项列表)。-L 标志设置你希望 GCC 来链接的库的路径。对于 32 位来说通常是 /usr/lib,不过,这依赖于你的系统是如何设置的,它可以是 /usr/lib32,甚至 /opt/usr/lib,或者任何你知道存放你的 32 位库的地方。

在代码编译后,查看你的构建的证据:

$ file ./dice32
dice: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs) [...]

接着,当然, ldd ./dice32 也会指向你的 32 位库。

不同的架构

在 64 位相同的处理器家族上允许 GCC 做出很多关于如何编译代码的假设来编译 32 位软件。如果你需要为完全不同的处理器编译,你必需安装适当的交叉构建实用程序。安装哪种实用程序取决于你正在编译的东西。这个过程比为相同的 CPU 家族编译更复杂一点。

当你为相同处理器家族交叉编译时,你可以期待找到与 32 位库集的相同的 64 位库集,因为你的 Linux 发行版是同时维护这二者的。当为一个完全不同的架构编译时,你可能不得不穷追你的代码所需要的库。你需要的版本可能不在你的发行版的存储库中,因为你的发行版可能不为你的目标系统提供软件包,或者它不在容易到达的位置提供所有的软件包。如果你正在编译的代码是你写的,那么你可能非常清楚它的依赖关系是什么,并清楚在哪里找到它们。如果代码是你下载的,并需要编译,那么你可能不熟悉它的要求。在这种情况下,研究正确编译代码需要什么(它们通常被列在 READMEINSTALL 文件中,当然也出现在源文件代码自身之中),然后收集需要的组件。

例如,如果你需要为 ARM 编译 C 代码,你必须首先在 Fedora 或 RHEL 上安装 gcc-arm-linux-gnu(32 位)或 gcc-aarch64-linux-gnu(64 位);或者,在 Ubuntu 上安装 arm-linux-gnueabi-gccbinutils-arm-linux-gnueabi。这提供你需要用来构建(至少)一个简单的 C 程序的命令和库。此外,你需要你的代码使用的任何库。你可以在惯常的位置(大多数系统上在 /usr/include)放置头文件,或者,你可以放置它们在一个你选择的目录,并使用 -I 选项将 GCC 指向它。

当编译时,不使用标准的 gccg++ 命令。作为代替,使用你安装的 GCC 实用程序。例如:

$ arm-linux-gnu-g++ dice.cpp \
  -I/home/seth/src/crossbuild/arm/cpp \
  -o armdice.bin

验证你构建的内容:

$ file armdice.bin
armdice.bin: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV) [...]

库和可交付结果

这是一个如何使用交叉编译的简单的示例。在真实的生活中,你的源文件代码可能产生的不止于一个二进制文件。虽然你可以手动管理,在这里手动管理可能不是好的正当理由。在我接下来的文章中,我将说明 GNU 自动工具,GNU 自动工具做了使你的代码可移植的大部分工作。


via: https://opensource.com/article/19/7/cross-compiling-gcc

作者:Seth Kenlon 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

cmd/compile 包含构成 Go 编译器主要的包。编译器在逻辑上可以被分为四个阶段,我们将简要介绍这几个阶段以及包含相应代码的包的列表。

在谈到编译器时,有时可能会听到 前端 front-end 后端 back-end 这两个术语。粗略地说,这些对应于我们将在此列出的前两个和后两个阶段。第三个术语 中间端 middle-end 通常指的是第二阶段执行的大部分工作。

请注意,go/parsergo/typesgo/* 系列的包与编译器无关。由于编译器最初是用 C 编写的,所以这些 go/* 包被开发出来以便于能够写出和 Go 代码一起工作的工具,例如 gofmtvet

需要澄清的是,名称 “gc” 代表 “ Go 编译器 Go compiler ”,与大写 GC 无关,后者代表 垃圾收集 garbage collection

1、解析

  • cmd/compile/internal/syntax 词法分析器 lexer 解析器 parser 语法树 syntax tree

在编译的第一阶段,源代码被标记化(词法分析)、解析(语法分析),并为每个源文件构造语法树(LCTT 译注:这里标记指 token,它是一组预定义的、能够识别的字符串,通常由名字和值构成,其中名字一般是词法的类别,如标识符、关键字、分隔符、操作符、文字和注释等;语法树,以及下文提到的 抽象语法树 Abstract Syntax Tree (AST),是指用树来表达程序设计语言的语法结构,通常叶子节点是操作数,其它节点是操作码)。

每个语法树都是相应源文件的确切表示,其中节点对应于源文件的各种元素,例如表达式、声明和语句。语法树还包括位置信息,用于错误报告和创建调试信息。

2、类型检查和 AST 变换

  • cmd/compile/internal/gc(创建编译器 AST, 类型检查 type-checking AST 变换 AST transformation

gc 包中包含一个继承自(早期)C 语言实现的版本的 AST 定义。所有代码都是基于它编写的,所以 gc 包必须做的第一件事就是将 syntax 包(定义)的语法树转换为编译器的 AST 表示法。这个额外步骤可能会在将来重构。

然后对 AST 进行类型检查。第一步是名字解析和类型推断,它们确定哪个对象属于哪个标识符,以及每个表达式具有的类型。类型检查包括特定的额外检查,例如“声明但未使用”以及确定函数是否会终止。

特定变换也基于 AST 完成。一些节点被基于类型信息而细化,例如把字符串加法从算术加法的节点类型中拆分出来。其它一些例子是 死代码消除 dead code elimination 函数调用内联 function call inlining 逃逸分析 escape analysis (LCTT 译注:逃逸分析是一种分析指针有效范围的方法)。

3、通用 SSA

  • cmd/compile/internal/gc(转换成 SSA)
  • cmd/compile/internal/ssa(SSA 相关的 环节 pass 和规则)

(LCTT 译注:许多常见高级语言的编译器无法通过一次扫描源代码或 AST 就完成所有编译工作,取而代之的做法是多次扫描,每次完成一部分工作,并将输出结果作为下次扫描的输入,直到最终产生目标代码。这里每次扫描称作一个 环节 pass ;最后一个环节之前所有的环节得到的结果都可称作中间表示法,本文中 AST、SSA 等都属于中间表示法。SSA,静态单赋值形式,是中间表示法的一种性质,它要求每个变量只被赋值一次且在使用前被定义)。

在此阶段,AST 将被转换为 静态单赋值 Static Single Assignment (SSA)形式,这是一种具有特定属性的低级 中间表示法 intermediate representation ,可以更轻松地实现优化并最终从它生成机器码。

在这个转换过程中,将完成 内置函数 function intrinsics 的处理。这些是特殊的函数,编译器被告知逐个分析这些函数并决定是否用深度优化的代码替换它们(LCTT 译注:内置函数指由语言本身定义的函数,通常编译器的处理方式是使用相应实现函数的指令序列代替对函数的调用指令,有点类似内联函数)。

在 AST 转化成 SSA 的过程中,特定节点也被低级化为更简单的组件,以便于剩余的编译阶段可以基于它们工作。例如,内建的拷贝被替换为内存移动,range 循环被改写为 for 循环。由于历史原因,目前这里面有些在转化到 SSA 之前发生,但长期计划则是把它们都移到这里(转化 SSA)。

然后,一系列机器无关的规则和编译环节会被执行。这些并不考虑特定计算机体系结构,因此对所有 GOARCH 变量的值都会运行。

这类通用的编译环节的一些例子包括,死代码消除、移除不必要的空值检查,以及移除无用的分支等。通用改写规则主要考虑表达式,例如将一些表达式替换为常量,优化乘法和浮点操作。

4、生成机器码

  • cmd/compile/internal/ssa(SSA 低级化和架构特定的环节)
  • cmd/internal/obj(机器码生成)

编译器中机器相关的阶段开始于“低级”的编译环节,该阶段将通用变量改写为它们的特定的机器码形式。例如,在 amd64 架构中操作数可以在内存中操作,这样许多 加载-存储 load-store 操作就可以被合并。

注意低级的编译环节运行所有机器特定的重写规则,因此当前它也应用了大量优化。

一旦 SSA 被“低级化”并且更具体地针对目标体系结构,就要运行最终代码优化的编译环节了。这包含了另外一个死代码消除的环节,它将变量移动到更靠近它们使用的地方,移除从来没有被读过的局部变量,以及 寄存器 register 分配。

本步骤中完成的其它重要工作包括 堆栈布局 stack frame layout ,它将堆栈偏移位置分配给局部变量,以及 指针活性分析 pointer liveness analysis ,后者计算每个垃圾收集安全点上的哪些堆栈上的指针仍然是活动的。

在 SSA 生成阶段结束时,Go 函数已被转换为一系列 obj.Prog 指令。它们被传递给汇编程序(cmd/internal/obj),后者将它们转换为机器码并输出最终的目标文件。目标文件还将包含反射数据,导出数据和调试信息。

扩展阅读

要深入了解 SSA 包的工作方式,包括它的环节和规则,请转到 cmd/compile/internal/ssa/README.md


via: https://github.com/golang/go/blob/master/src/cmd/compile/README.md

作者:mvdan 译者:stephenxs 校对:pityonline, wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Jack 将带你在 Ubuntu 16.04 服务器上走过内核编译之旅。

曾经有一段时间,升级 Linux 内核让很多用户打心里有所畏惧。在那个时候,升级内核包含了很多步骤,也需要很多时间。现在,内核的安装可以轻易地通过像 apt 这样的包管理器来处理。通过添加特定的仓库,你能很轻易地安装实验版本的或者指定版本的内核(比如针对音频产品的实时内核)。

考虑一下,既然升级内核如此容易,为什么你不愿意自行编译一个呢?这里列举一些可能的原因:

  • 你想要简单了解编译内核的过程
  • 你需要启用或者禁用内核中特定的选项,因为它们没有出现在标准选项里
  • 你想要启用标准内核中可能没有添加的硬件支持
  • 你使用的发行版需要你编译内核
  • 你是一个学生,而编译内核是你的任务

不管出于什么原因,懂得如何编译内核是非常有用的,而且可以被视作一个通行权。当我第一次编译一个新的 Linux 内核(那是很久以前了),然后尝试从它启动,我从中(系统马上就崩溃了,然后不断地尝试和失败)感受到一种特定的兴奋。

既然这样,让我们来实验一下编译内核的过程。我将使用 Ubuntu 16.04 Server 来进行演示。在运行了一次常规的 sudo apt upgrade 之后,当前安装的内核版本是 4.4.0-121。我想要升级内核版本到 4.17, 让我们小心地开始吧。

有一个警告:强烈建议你在虚拟机里实验这个过程。基于虚拟机,你总能创建一个快照,然后轻松地从任何问题中回退出来。不要在产品机器上使用这种方式升级内核,除非你知道你在做什么。

下载内核

我们要做的第一件事是下载内核源码。在 Kernel.org 找到你要下载的所需内核的 URL。找到 URL 之后,使用如下命令(我以 4.17 RC2 内核为例) 来下载源码文件:

wget https://git.kernel.org/torvalds/t/linux-4.17-rc2.tar.gz

在下载期间,有一些事需要去考虑。

安装需要的环境

为了编译内核,我们首先得安装一些需要的环境。这可以通过一个命令来完成:

sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison

务必注意:你将需要至少 12 GB 的本地可用磁盘空间来完成内核的编译过程。因此你必须确保有足够的空间。

解压源码

在新下载的内核所在的文件夹下,使用该命令来解压内核:

tar xvzf linux-4.17-rc2.tar.gz

使用命令 cd linux-4.17-rc2 进入新生成的文件夹。

配置内核

在正式编译内核之前,我们首先必须配置需要包含哪些模块。实际上,有一些非常简单的方式来配置。使用一个命令,你能拷贝当前内核的配置文件,然后使用可靠的 menuconfig 命令来做任何必要的更改。使用如下命令来完成:

cp /boot/config-$(uname -r) .config

现在你有一个配置文件了,输入命令 make menuconfig。该命令将打开一个配置工具(图 1),它可以让你遍历每个可用模块,然后启用或者禁用你需要或者不需要的模块。

 title=

图 1: 运行中的 make menuconfig

很有可能你会禁用掉内核中的一个重要部分,所以在 menuconfig 期间小心地一步步进行。如果你对某个选项不确定,不要去管它。或者更好的方法是使用我们拷贝的当前运行的内核的配置文件(因为我们知道它可以工作)。一旦你已经遍历了整个配置列表(它非常长),你就准备好开始编译了。

编译和安装

现在是时候去实际地编译内核了。第一步是使用 make 命令去编译。调用 make 命令然后回答必要的问题(图 2)。这些问题取决于你将升级的现有内核以及升级后的内核。相信我,将会有非常多的问题要回答,因此你得预留大量的时间。

 title=

图 2: 回答 make 命令的问题

回答了长篇累牍的问题之后,你就可以用如下的命令安装那些之前启用的模块:

make modules_install

又来了,这个命令将耗费一些时间,所以要么坐下来看着编译输出,或者去做些其他事(因为编译期间不需要你的输入)。可能的情况是,你想要去进行别的任务(除非你真的喜欢看着终端界面上飞舞而过的输出)。

现在我们使用这个命令来安装内核:

sudo make install

又一次,另一个将要耗费大量可观时间的命令。事实上,make install 命令将比 make modules_install 命令花费更多的时间。去享用午餐,配置一个路由器,将 Linux 安装在一些服务器上,或者小睡一会吧。

启用内核作为引导

一旦 make install 命令完成了,就是时候将内核启用来作为引导。使用这个命令来实现:

sudo update-initramfs -c -k 4.17-rc2

当然,你需要将上述内核版本号替换成你编译完的。当命令执行完毕后,使用如下命令来更新 grub:

sudo update-grub

现在你可以重启系统并且选择新安装的内核了。

恭喜!

你已经编译了一个 Linux 内核!它是一项耗费时间的活动;但是,最终你的 Linux 发行版将拥有一个定制的内核,同时你也将拥有一项被许多 Linux 管理员所倾向忽视的重要技能。

从 Linux 基金会和 edX 提供的免费 “Introduction to Linux” 课程来学习更多的 Linux 知识。


via: https://www.linux.com/learn/intro-to-linux/2018/4/how-compile-linux-kernel-0

作者:Jack Wallen 选题:lujun9972 译者:icecoobe 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

目的

使用 GNU Stow 轻松管理从源代码安装的程序和点文件(LCTT 译注: 点文件 dotfile ,即以 . 开头的文件,在 *nix 下默认为隐藏文件,常用于存储程序的配置信息。)

要求

  • root 权限

难度

简单

约定

  • # - 给定的命令要求直接以 root 用户身份或使用 sudo 命令以 root 权限执行
  • $ - 给定的命令将作为普通的非特权用户来执行

介绍

有时候我们必须从源代码安装程序,因为它们也许不能通过标准渠道获得,或者我们可能需要特定版本的软件。 GNU Stow 是一个非常不错的 符号链接工厂 symlinks factory 程序,它可以帮助我们保持文件的整洁,易于维护。

获得 stow

你的 Linux 发行版本很可能包含 stow,例如在 Fedora,你安装它只需要:

# dnf install stow

在 Ubuntu/Debian 中,安装 stow 需要执行:

# apt install stow

在某些 Linux 发行版中,stow 在标准库中是不可用的,但是可以通过一些额外的软件源(例如 RHEL 和 CentOS7 中的EPEL )轻松获得,或者,作为最后的手段,你可以从源代码编译它。只需要很少的依赖关系。

从源代码编译

最新的可用 stow 版本是 2.2.2。源码包可以在这里下载:https://ftp.gnu.org/gnu/stow/

一旦你下载了源码包,你就必须解压它。切换到你下载软件包的目录,然后运行:

$ tar -xvpzf stow-2.2.2.tar.gz

解压源文件后,切换到 stow-2.2.2 目录中,然后编译该程序,只需运行:

$ ./configure
$ make

最后,安装软件包:

# make install

默认情况下,软件包将安装在 /usr/local/ 目录中,但是我们可以改变它,通过配置脚本的 --prefix 选项指定目录,或者在运行 make install 时添加 prefix="/your/dir"

此时,如果所有工作都按预期工作,我们应该已经在系统上安装了 stow

stow 是如何工作的?

stow 背后主要的概念在程序手册中有很好的解释:

Stow 使用的方法是将每个软件包安装到自己的目录树中,然后使用符号链接使它看起来像文件一样安装在公共的目录树中

为了更好地理解这个软件的运作,我们来分析一下它的关键概念:

stow 文件目录

stow 目录是包含所有 stow 软件包的根目录,每个包都有自己的子目录。典型的 stow 目录是 /usr/local/stow:在其中,每个子目录代表一个软件包。

stow 软件包

如上所述,stow 目录包含多个“软件包”,每个软件包都位于自己单独的子目录中,通常以程序本身命名。包就是与特定软件相关的文件和目录列表,作为一个实体进行管理。

stow 目标目录

stow 目标目录解释起来是一个非常简单的概念。它是包文件应该安装到的目录。默认情况下,stow 目标目录被视作是调用 stow 的目录。这种行为可以通过使用 -t 选项( --target 的简写)轻松改变,这使我们可以指定一个替代目录。

一个实际的例子

我相信一个好的例子胜过 1000 句话,所以让我来展示 stow 如何工作。假设我们想编译并安装 libx264,首先我们克隆包含其源代码的仓库:

$ git clone git://git.videolan.org/x264.git

运行该命令几秒钟后,将创建 x264 目录,它将包含准备编译的源代码。我们切换到 x264 目录中并运行 configure 脚本,将 --prefix 指定为 /usr/local/stow/libx264 目录。

$ cd x264 && ./configure --prefix=/usr/local/stow/libx264

然后我们构建该程序并安装它:

$ make
# make install

x264 目录应该创建在 stow 目录内:它包含了所有通常直接安装在系统中的东西。 现在,我们所要做的就是调用 stow。 我们必须从 stow 目录内运行这个命令,通过使用 -d 选项来手动指定 stow 目录的路径(默认为当前目录),或者通过如前所述用 -t 指定目标。我们还应该提供要作为参数存储的软件包的名称。 在这里,我们从 stow 目录运行程序,所以我们需要输入的内容是:

# stow libx264

libx264 软件包中包含的所有文件和目录现在已经在调用 stow 的父目录 (/usr/local) 中进行了符号链接,因此,例如在 /usr/local/ stow/x264/bin 中包含的 libx264 二进制文件现在符号链接在 /usr/local/bin 之中,/usr/local/stow/x264/etc 中的文件现在符号链接在 /usr/local/etc 之中等等。通过这种方式,系统将显示文件已正常安装,并且我们可以容易地跟踪我们编译和安装的每个程序。要反转该操作,我们只需使用 -D 选项:

# stow -d libx264

完成了!符号链接不再存在:我们只是“卸载”了一个 stow 包,使我们的系统保持在一个干净且一致的状态。 在这一点上,我们应该清楚为什么 stow 还可以用于管理点文件。 通常的做法是在 git 仓库中包含用户特定的所有配置文件,以便轻松管理它们并使它们在任何地方都可用,然后使用 stow 将它们放在适当位置,如放在用户主目录中。

stow 还会阻止你错误地覆盖文件:如果目标文件已经存在,并且没有指向 stow 目录中的包时,它将拒绝创建符号链接。 这种情况在 stow 术语中称为冲突。

就是这样!有关选项的完整列表,请参阅 stow 帮助页,并且不要忘记在评论中告诉我们你对此的看法。


via: https://linuxconfig.org/how-to-use-gnu-stow-to-manage-programs-installed-from-source-and-dotfiles

作者:Egidio Docile 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我最近在 docker-library/php 仓库中关闭了大量问题,最老的(并且是最长的)讨论之一是关于安装编译扩展的依赖关系,我写了一个中等篇幅的评论解释了我如何用常规的方式为我想要的软件进行 Docker 化的。

我要在这里复制大部分的评论内容,或许扩展一点点,以便有一个更好的/更干净的链接!

我第一步是编写 Dockerfile 的原始版本:下载源码,运行 ./configure && make 等,清理。然后我尝试构建我的原始版本,并希望在这过程中看到错误消息。(对,真的!)

错误信息通常以 error: could not find "xyz.h"error: libxyz development headers not found 的形式出现。

如果我在 Debian 中构建,我会输入 https://packages.debian.org/file:xyz.h(用错误信息中头文件的名称替换 “xyz.h”),或者在谷歌中搜索像 “xyz.h debian” 这样的东西,找出我需要的包的名称。

如果我在 Alpine 中构建,我将使用 https://pkgs.alpinelinux.org/contents 进行类似的搜索。

“libxyz development headers” 在某种程度上也是一样的,但是根据我的经验,对于这些用 Google 对开发者来说效果更好,因为不同的发行版和项目会以不同的名字来调用这些开发包,所以有时候更难确切的知道哪一个是“正确”的。

当我得到包名后,我将这个包名称添加到我的 Dockerfile 中,清理之后,然后重复操作。最终通常会构建成功。偶尔我发现某些库不在 Debian 或 Alpine 中,或者是不够新的,由此我必须从源码构建它,但这些情况在我的经验中很少见 —— 因人而异。

我还会经常查看 Debian(通过 https://sources.debian.org)或Alpine(通过https://git.alpinelinux.org/cgit/aports/tree)我要编译的软件包源码,特别关注 Build-Depends(如 php7.0=7.0.26-1debian/control 文件)以及/或者 makedepends (如 php7APKBUILD 文件)用于包名线索。

就我个人而言,我觉得这种侦探工作很有趣,也很有收获,但我意识到我可能是一个独特的生物。我偶尔使用的另一个技术是看是否有人已经 Docker 化了我想要的东西,这样我可以直接从他们的 Dockerfile 中知道我需要安装的东西。

对于特定的 PHP 扩展,几乎总是有人已经想出对于这个或那个模块需要的依赖,而我所要做的就是做一些轻量的工作找出它们。

不管怎样,这就是我的方法!希望这个有帮助,玩得愉快!


via: https://tianon.github.io/post/2017/12/26/dockerize-compiled-software.html

作者:Tianon Gravi 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

How to install software from source code

简介:这篇文章详细介绍了在 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("%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[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
[...]
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中国 荣誉推出