标签 编译 下的文章

这是一篇如何从源码编译 GNOME 的快速指南,包括 Shell、mutter 和一些原生应用。

在编译之前,你需要确保一些事情,因为以下编译直接来自 Gitlab 的主分支,其中包含一些开发包。

通常,你可以选择在任何 Linux 发行版中编译。但是我建议使用 Fedora Rawhide(Fedora 的开发分支,用于将来的发布)。

另外,请勿在稳定系统中尝试此操作。因为操作可能出错,所以你可能最终得到损坏的系统。

总而言之,你需要以下内容来从源码编译 GNOME。

  • 测试环境(虚拟机 或测试系统)。
  • Fedora Rawhide 发行版(推荐,从此处下载)。
  • 确保你的发行版是最新的。
  • 你已登录 X.org 会话。

我不建议你在 Wayland 会话中进行编译,因为你会遇到问题。

从源码编译 GNOME

GNOME 桌面是一个基于其功能的软件包集合。Linux 发行版的桌面组件工作于窗口管理器和 shell 之下。

因此,对于 GNOME,我将首先编译 mutter – 它是 GNOME Shell 的窗口管理器。然后进行 GNOME Shell 的编译。最后,我将编译一些原生应用。

我将使用 meson 构建系统进行编译。meson 是一个漂亮的构建系统,快速且用户友好。

编译 mutter

打开终端并安装 GNOME Shell 和 mutter 所需的软件包。

sudo dnf build-dep mutter gnome-shell

在主目录(或你想要的任何地方)中创建演示目录。

cd ~
mkdir demo
cd demo

从 Gitlab 克隆 mutter 的主分支。

git clone https://gitlab.gnome.org/GNOME/mutter

进入克隆目录,然后使用以下 meson 命令来准备构建文件。默认情况下,meson 使用 /usr/local 用于构建文件。但是,你也可以使用前缀开关将输出重定向到特定文件夹(如下所示)。

cd mutter
meson _build --prefix=/usr

Compile Mutter for GNOME

使用以下命令在构建完成时,将 mutter 安装在到系统中。

sudo ninja install -C _build

编译 GNOME Shell

GNOME Shell 和其他软件包的编译方法类似。首先,从 GitLab 克隆 GNOME Shell 主仓库,然后进行编译和安装。你可以按照下面的命令依次进行。

在 GNOME Shell 中,你需要两个依赖项。它们是 asciidocsassc 。请在构建 GNOME Shell 之前安装它们。

sudo dnf install asciidoc
sudo dnf install sassc

安装完这些依赖项后,按照下面的命令来构建和安装 GNOME Shell。在运行这个命令之前,请确保你回到 demo 文件夹(我在第一步创建的)。

git clone https://gitlab.gnome.org/GNOME/gnome-shellcd gnome-shellmeson _build --prefix=/usrsudo ninja install -C _build

运行 GNOME Shell

编译完成后,你可以尝试重新启动 GNOME Shell 来查看来自主分支的变化。

在重启之前,正如我之前提到的,确保你处于 X.Org 会话中。按 ALT+F2 并输入 r。然后按回车键。这个命令将重启 GNOME Shell。

Restart GNOME Shell (X11)

恭喜你! 你已经成功地编译了 GNOME Shell 和 Mutter。

现在,是时候编译一些 GNOME 原生应用了。

编译 GNOME 原生应用

这些步骤对于 GNOME 或任何应用的所有源码都是一样的。你需要改变仓库的名字。因此,这里有一些编译必要的 GNOME 原生应用的命令示例。

Files(Nautilus)

git clone https://gitlab.gnome.org/GNOME/nautilus/cd gnome-shellmeson _build --prefix=/usrsudo ninja install -C _build

GNOME 软件商店

git clone https://gitlab.gnome.org/GNOME/gnome-software/cd gnome-shellmeson _build --prefix=/usrsudo ninja install -C _build

GNOME 控制中心

git clone https://gitlab.gnome.org/GNOME/gnome-control-center/cd gnome-shellmeson _build --prefix=/usrsudo ninja install -C _build

FAQ

  1. 使用上述步骤,你可以编译任何源码分支。不仅仅是 GNOME。
  2. GitLab 服务器有时很慢,克隆一个仓库可能需要较长的时间。如果 git clone 失败,我建议你再试一次。

结束语

我希望这个小小的高级教程能够帮助你在新的 GNOME 功能出现在 GNOME 每日构建系统之前尝试它。既然你编译了,你也可以为测试新的 GNOME 功能做出贡献,并在 GitLab 问题页面上报告任何特定包的 bug 或问题。

这篇文章是开源应用编译系列的第一篇文章。请继续关注更多开源应用的编译文章。

另外,请让我在下面的评论栏中知道你的评论、建议,或者你在使用这些说明时遇到的任何错误。

干杯。


via: https://www.debugpoint.com/2022/05/compile-gnome-source/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

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

带你一窥生成二进制文件步骤的幕后,以便在出现一些错误时,你知道如何逐步解决问题。

C 语言广为人知,深受新老程序员的好评。使用 C 语言编写的源文件代码,使用了标准的英语术语,因而人们可以方便阅读。然而,计算机只能理解二进制代码。为将代码转换为机器语言,你需要使用一种被称为 编译器 compiler 的工具。

最常见的编译器是 GCC( GNU 编译器集 GNU Compiler Collection )。编译过程涉及到一系列的中间步骤及相关工具。

安装 GCC

为验证在你的系统上是否已经安装了 GCC,使用 gcc 命令:

$ gcc --version

如有必要,使用你的软件包管理器来安装 GCC。在基于 Fedora 的系统上,使用 dnf

$ sudo dnf install gcc libgcc

在基于 Debian 的系统上,使用 apt

$ sudo apt install build-essential

在安装后,如果你想查看 GCC 的安装位置,那么使用:

$ whereis gcc

演示使用 GCC 来编译一个简单的 C 程序

这里有一个简单的 C 程序,用于演示如何使用 GCC 来编译。打开你最喜欢的文本编辑器,并在其中粘贴这段代码:

// hellogcc.c
#include <stdio.h>

int main() {
    printf("Hello, GCC!\n");
    return 0;
}

保存文件为 hellogcc.c ,接下来编译它:

$ ls
hellogcc.c

$ gcc hellogcc.c

$ ls -1
a.out
hellogcc.c

如你所见,a.out 是编译后默认生成的二进制文件。为查看你所新编译的应用程序的输出,只需要运行它,就像你运行任意本地二进制文件一样:

$ ./a.out
Hello, GCC!

命名输出的文件

文件名称 a.out 是非常莫名其妙的,所以,如果你想具体指定可执行文件的名称,你可以使用 -o 选项:

(LCTT 译注:注意这和最近 Linux 内核废弃的 a.out 格式无关,只是名字相同,这里生成的 a.out 是 ELF 格式的 —— 也不知道谁给起了个 a.out 这破名字,在我看来,默认输出文件名就应该是去掉了 .c 扩展名后的名字。by wxy)

$ gcc -o hellogcc hellogcc.c

$ ls
a.out hellogcc hellogcc.c

$ ./hellogcc
Hello, GCC!

当开发一个需要编译多个 C 源文件文件的大型应用程序时,这种选项是很有用的。

在 GCC 编译中的中间步骤

编译实际上有四个步骤,即使在简单的用例中 GCC 自动执行了这些步骤。

  1. 预处理 Pre-Processing :GNU 的 C 预处理器(cpp)解析头文件(#include 语句),展开 macros 定义(#define 语句),并使用展开的源文件代码来生成一个中间文件,如 hellogcc.i
  2. 编译 Compilation :在这个期间中,编译器将预处理的源文件代码转换为指定 CPU 架构的汇编代码。由此生成是汇编文件使用一个 .s 扩展名来命名,如在这个示例中的 hellogcc.s
  3. 汇编 Assembly :汇编程序(as)将汇编代码转换为目标机器代码,放在目标文件中,例如 hellogcc.o
  4. 链接 Linking :链接器(ld)将目标代码和库代码链接起来生成一个可执行文件,例如 hellogcc

在运行 GCC 时,可以使用 -v 选项来查看每一步的细节:

$ gcc -v -o hellogcc hellogcc.c

Compiler flowchart

手动编译代码

体验编译的每个步骤可能是很有用的,因此在一些情况下,你不需要 GCC 完成所有的步骤。

首先,除源文件文件以外,删除在当前文件夹下生成的文件。

$ rm a.out hellogcc.o

$ ls
hellogcc.c

预处理器

首先,启动预处理器,将其输出重定向为 hellogcc.i

$ cpp hellogcc.c > hellogcc.i

$ ls
hellogcc.c hellogcc.i

查看输出文件,并注意一下预处理器是如何包含头文件和扩展宏中的源文件代码的。

编译器

现在,你可以编译代码为汇编代码。使用 -S 选项来设置 GCC 只生成汇编代码:

$ gcc -S hellogcc.i

$ ls
hellogcc.c hellogcc.i hellogcc.s

$ cat hellogcc.s

查看汇编代码,来看看生成了什么。

汇编

使用你刚刚所生成的汇编代码来创建一个目标文件:

$ as -o hellogcc.o hellogcc.s

$ ls
hellogcc.c hellogcc.i hellogcc.o hellogcc.s

链接

要生成一个可执行文件,你必须将对象文件链接到它所依赖的库。这并不像前面的步骤那么简单,但它却是有教育意义的:

$ ld -o hellogcc hellogcc.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: hellogcc.o: in function `main`:
hellogcc.c:(.text+0xa): undefined reference to `puts'

在链接器查找完 libc.so 库后,出现一个引用 undefined puts 错误。你必须找出适合的链接器选项来链接必要的库以解决这个问题。这不是一个小技巧,它取决于你的系统的布局。

在链接时,你必须链接代码到 核心运行时 core runtime (CRT)目标,这是一组帮助二进制可执行文件启动的子例程。链接器也需要知道在哪里可以找到重要的系统库,包括 libclibgcc,尤其是其中的特殊的开始和结束指令。这些指令可以通过 --start-group--end-group 选项来分隔,或者使用指向 crtbegin.ocrtend.o 的路径。

这个示例使用了 RHEL 8 上的路径,因此你可能需要依据你的系统调整路径。

$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
    -o hello \
    /usr/lib64/crt1.o /usr/lib64/crti.o \
    --start-group \
        -L/usr/lib/gcc/x86_64-redhat-linux/8 \
        -L/usr/lib64 -L/lib64 hello.o \
        -lgcc \
        --as-needed -lgcc_s \
        --no-as-needed -lc -lgcc \
    --end-group \
    /usr/lib64/crtn.o

在 Slackware 上,同样的链接过程会使用一组不同的路径,但是,你可以看到这其中的相似之处:

$ ld -static -o hello \
    -L/usr/lib64/gcc/x86_64-slackware-linux/11.2.0/ \
    /usr/lib64/crt1.o /usr/lib64/crti.o hello.o /usr/lib64/crtn.o \
    --start-group \
        -lc -lgcc -lgcc_eh \
    --end-group

现在,运行由此生成的可执行文件:

$ ./hello
Hello, GCC!

一些有用的实用程序

下面是一些帮助检查文件类型、 符号表 symbol tables 和链接到可执行文件的库的实用程序。

使用 file 实用程序可以确定文件的类型:

$ file hellogcc.c
hellogcc.c: C source, ASCII text

$ file hellogcc.o
hellogcc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

$ file hellogcc
hellogcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb76b241d7d00871806e9fa5e814fee276d5bd1a, for GNU/Linux 3.2.0, not stripped

对目标文件使用 nm 实用程序可以列出 符号表 symbol tables

$ nm hellogcc.o
0000000000000000 T main
             U puts

使用 ldd 实用程序来列出动态链接库:

$ ldd hellogcc
linux-vdso.so.1 (0x00007ffe3bdd7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f223395e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2233b7e000)

总结

在这篇文章中,你了解到了 GCC 编译中的各种中间步骤,和检查文件类型、 符号表 symbol tables 和链接到可执行文件的库的实用程序。在你下次使用 GCC 时,你将会明白它为你生成一个二进制文件所要做的步骤,并且当出现一些错误时,你会知道如何逐步处理解决问题。


via: https://opensource.com/article/22/5/gnu-c-compiler

作者:Jayashree Huttanagoudar 选题:lkxed 译者:robsean 校对:wxy

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

这些方便的 Go 构建选项可以帮助你更好地理解 Go 的编译过程。

学习一门新的编程语言最令人欣慰的部分之一,就是最终运行了一个可执行文件,并获得预期的输出。当我开始学习 Go 这门编程语言时,我先是阅读一些示例程序来熟悉语法,然后是尝试写一些小的测试程序。随着时间的推移,这种方法帮助我熟悉了编译和构建程序的过程。

Go 的构建选项提供了更好地控制构建过程的方法。它们还可以提供额外的信息,帮助把这个过程分成更小的部分。在这篇文章中,我将演示我所使用的一些选项。注意:我使用的“ 构建 build ”和“ 编译 compile ”这两个词是同一个意思。

开始使用 Go

我使用的 Go 版本是 1.16.7。但是,这里给出的命令应该也能在最新的版本上运行。如果你没有安装 Go,你可以从 Go 官网 上下载它,并按照说明进行安装。你可以通过打开一个命令提示符,并键入下面的命令来验证你所安装的版本:

$ go version

你应该会得到类似下面这样的输出,具体取决于你安装的版本:

go version go1.16.7 linux/amd64

基本的 Go 程序的编译和执行方法

我将从一个在屏幕上简单打印 “Hello World” 的 Go 程序示例开始,就像下面这样:

$ cat hello.go
package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

在讨论更高级的选项之前,我将解释如何编译这个 Go 示例程序。我使用了 build 命令,后面跟着 Go 程序的源文件名,本例中是 hello.go,就像下面这样:

$ go build hello.go

如果一切工作正常,你应该看到在你的当前目录下创建了一个名为 hello 的可执行文件。你可以通过使用 file 命令验证它是 ELF 二进制可执行格式(在 Linux 平台上)。你也可以直接执行它,你会看到它输出 “Hello World”。

$ ls
hello  hello.go

$ file ./hello
./hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ ./hello
Hello World

Go 提供了一个方便的 run 命令,以便你只是想看看程序是否能正常工作,并获得预期的输出,而不想生成一个最终的二进制文件。请记住,即使你在当前目录中没有看到可执行文件,Go 仍然会在某个地方编译并生成可执行文件并运行它,然后把它从系统中删除。我将在本文后面的章节中解释。

$ go run hello.go
Hello World

$ ls
hello.go

更多细节

上面的命令就像一阵风一样,一下子就运行完了我的程序。然而,如果你想知道 Go 在编译这些程序的过程中做了什么,Go 提供了一个 -x 选项,它可以打印出 Go 为产生这个可执行文件所做的一切。

简单看一下你就会发现,Go 在 /tmp 内创建了一个临时工作目录,并生成了可执行文件,然后把它移到了 Go 源程序所在的当前目录。

$ go build -x hello.go

WORK=/tmp/go-build1944767317
mkdir -p $WORK/b001/

<< snip >>

mkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK \
/b001/exe/a.out -importcfg $WORK/b001 \
/importcfg.link -buildmode=exe -buildid=K26hEYzgDkqJjx2Hf-wz/\
nDueg0kBjIygx25rYwbK/W-eJaGIOdPEWgwC6o546 \
/K26hEYzgDkqJjx2Hf-wz -extld=gcc /root/.cache/go-build /cc \
/cc72cb2f4fbb61229885fc434995964a7a4d6e10692a23cc0ada6707c5d3435b-d
/usr/lib/golang/pkg/tool/linux_amd64/buildid -w $WORK \
/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out hello
rm -r $WORK/b001/

这有助于解决在程序运行后却在当前目录下没有生成可执行文件的谜团。使用 -x 显示可执行文件确实在 /tmp 工作目录下创建并被执行了。然而,与 build 命令不同的是,可执行文件并没有移动到当前目录,这使得看起来没有可执行文件被创建。

$ go run -x hello.go


mkdir -p $WORK/b001/exe/
cd .
/usr/lib/golang/pkg/tool/linux_amd64/link -o $WORK/b001 \
/exe/hello -importcfg $WORK/b001/importcfg.link -s -w -buildmode=exe -buildid=hK3wnAP20DapUDeuvAAS/E_TzkbzwXz6tM5dEC8Mx \
/7HYBzuaDGVdaZwSMEWAa/hK3wnAP20DapUDeuvAAS -extld=gcc \
/root/.cache/go-build/75/ \
7531fcf5e48444eed677bfc5cda1276a52b73c62ebac3aa99da3c4094fa57dc3-d
$WORK/b001/exe/hello
Hello World

模仿编译而不产生可执行文件

假设你不想编译程序并产生一个实际的二进制文件,但你确实想看到这个过程中的所有步骤。你可以通过使用 -n 这个构建选项来做到这一点,该选项会打印出通常的执行步骤,而不会实际创建二进制文件。

$ go build -n hello.go

保存临时目录

很多工作都发生在 /tmp 工作目录中,一旦可执行文件被创建和运行,它就会被删除。但是如果你想看看哪些文件是在编译过程中创建的呢?Go 提供了一个 -work 选项,它可以在编译程序时使用。-work 选项除了运行程序外,还打印了工作目录的路径,但它并不会在这之后删除工作目录,所以你可以切换到该目录,检查在编译过程中创建的所有文件。

$ go run -work hello.go
WORK=/tmp/go-build3209320645
Hello World

$ find /tmp/go-build3209320645
/tmp/go-build3209320645
/tmp/go-build3209320645/b001
/tmp/go-build3209320645/b001/importcfg.link
/tmp/go-build3209320645/b001/exe
/tmp/go-build3209320645/b001/exe/hello

$ /tmp/go-build3209320645/b001/exe/hello
Hello World

其他编译选项

如果说,你想手动编译程序,而不是使用 Go 的 buildrun 这两个方便的命令,最后得到一个可以直接由你的操作系统(这里指 Linux)运行的可执行文件。那么,你该怎么做呢?这个过程可以分为两部分:编译和链接。你可以使用 tool 选项来看看它是如何工作的。

首先,使用 tool compile 命令产生结果的 ar 归档文件,它包含了 .o 中间文件。接下来,对这个 hello.o 文件执行 tool link 命令,产生最终的可执行文件,然后你就可以运行它了。

$ go tool compile hello.go

$ file hello.o
hello.o: current ar archive

$ ar t hello.o
__.PKGDEF
_go_.o

$ go tool link -o hello hello.o

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ ./hello
Hello World

如果你想进一步查看基于 hello.o 文件产生可执行文件的链接过程,你可以使用 -v 选项,它会搜索每个 Go 可执行文件中包含的 runtime.a 文件。

$ go tool link -v -o hello hello.o
HEADER = -H5 -T0x401000 -R0x1000
searching for runtime.a in /usr/lib/golang/pkg/linux_amd64/runtime.a
82052 symbols, 18774 reachable
        1 package symbols, 1106 hashed symbols, 77185 non-package symbols, 3760 external symbols
81968 liveness data

交叉编译选项

现在我已经解释了 Go 程序的编译过程,接下来,我将演示 Go 如何通过在实际的 build 命令之前提供 GOOSGOARCH 这两个环境变量,来允许你构建针对不同硬件架构和操作系统的可执行文件。

这有什么用呢?举个例子,你会发现为 ARM(arch64)架构制作的可执行文件不能在英特尔(x86\_64)架构上运行,而且会产生一个 Exec 格式错误。

下面的这些选项使得生成跨平台的二进制文件变得小菜一碟:

$ GOOS=linux GOARCH=arm64 go build hello.go

$ file ./hello
./hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped

$ ./hello
bash: ./hello: cannot execute binary file: Exec format error

$ uname -m
x86_64

你可以阅读我之前的博文,以更多了解我在 使用 Go 进行交叉编译 方面的经验。

查看底层汇编指令

源代码并不会直接转换为可执行文件,尽管它生成了一种中间汇编格式,然后最终被组装为可执行文件。在 Go 中,这被映射为一种中间汇编格式,而不是底层硬件汇编指令。

要查看这个中间汇编格式,请在使用 build 命令时,提供 -gcflags 选项,后面跟着 -S。这个命令将会显示使用到的汇编指令:

$ go build -gcflags="-S" hello.go
# command-line-arguments
"".main STEXT size=138 args=0x0 locals=0x58 funcid=0x0
        0x0000 00000 (/test/hello.go:5) TEXT    "".main(SB), ABIInternal, $88-0
        0x0000 00000 (/test/hello.go:5) MOVQ    (TLS), CX
        0x0009 00009 (/test/hello.go:5) CMPQ    SP, 16(CX)
        0x000d 00013 (/test/hello.go:5) PCDATA  $0, $-2
        0x000d 00013 (/test/hello.go:5) JLS     128

<< snip >>

你也可以使用 objdump -s 选项,来查看已经编译好的可执行程序的汇编指令,就像下面这样:

$ ls
hello  hello.go

$ go tool objdump -s main.main hello
TEXT main.main(SB) /test/hello.go
  hello.go:5            0x4975a0                64488b0c25f8ffffff      MOVQ FS:0xfffffff8, CX                  
  hello.go:5            0x4975a9                483b6110                CMPQ 0x10(CX), SP                       
  hello.go:5            0x4975ad                7671                    JBE 0x497620                            
  hello.go:5            0x4975af                4883ec58                SUBQ $0x58, SP                          
  hello.go:6            0x4975d8                4889442448              MOVQ AX, 0x48(SP)                       

<< snip >>

分离二进制文件以减少其大小

Go 的二进制文件通常比较大。例如, 一个简单的 “Hello World” 程序将会产生一个 1.9M 大小的二进制文件。

$ go build hello.go
$
$ du -sh hello
1.9M    hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
$

为了减少生成的二进制文件的大小,你可以分离执行过程中不需要的信息。使用 -ldflags-s -w 选项可以使生成的二进制文件略微变小为 1.3M。

$ go build -ldflags="-s -w" hello.go
$
$ du -sh hello
1.3M    hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$

总结

我希望这篇文章向你介绍了一些方便的 Go 编译选项,同时帮助了你更好地理解 Go 编译过程。关于构建过程的其他信息和其他有趣的选项,请参考 Go 命令帮助:

$ go help build

题图由 Ashraf ChembanPixabay 上发布。


via: https://opensource.com/article/22/4/go-build-options

作者:Gaurav Kamathe 选题:lkxed 译者:lkxed 校对:wxy

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

你不需要知道如何写或读代码就可以编译它。

 title=

安装软件的方法有很多,但开源让你有了一个其他地方所没有的选择:你可以自己编译代码。编译源代码的经典三步流程是:

$ ./configure
$ make
$ sudo make install

由于有了这些命令,你可能会惊讶地发现,你不需要知道如何写代码,甚至不需要读代码就可以编译它。

安装用来构建软件的命令

由于这是你第一次进行编译,所以有一个一次性的准备步骤,即安装用于构建软件的命令。具体来说,你需要一个编译器。编译器(比如 GCC 或 LLVM)可以将像这样的源代码:

#include <iostream>

using namespace std;

int main() {
  cout << "hello world";
}

变成 机器语言,即 CPU 用来处理信息的指令。你可以看一下机器代码,但它对你来说没有任何意义(除非你是一个 CPU)。

你可以使用你的软件包管理器安装 GNU 编译器集合(GCC)和 LLVM 编译器,以及其他在 Fedora、CentOS、Mageia 和类似发行版上进行编译的基本命令:

$ sudo dnf install @development clang

在 Debian、Elementary、Mint 和类似发行版上命令如下:

$ sudo apt install build-essential clang

在你的系统设置好后,有几项你每次编译软件时都要重复进行的任务:

  1. 下载源代码
  2. 展开源代码的存档文件
  3. 编译

你已经有了所有你需要的命令,所以现在你需要一些软件来编译。

1、下载源代码

获得一个应用程序的源代码和获得任何可下载的软件一样。你得去一个网站或一个代码管理网站,如 GitLab、SourceForge 或 GitHub。 通常情况下,开源软件既有正在进行的工作(“ 当前 current ”或 “ 每夜 nightly ”)的构建版本,也有打包的“ 稳定 stable ”发布版本。在可能的情况下,使用稳定版本,除非你有理由相信,或者对代码有足够的了解,能够在出现故障时修复。术语“稳定版”表明这些代码经过了测试,而且该应用程序的程序员对代码有足够的信心,从而将其打包成 .zip.tar 归档,给了它一个官方编号,有时还有一个发布名称,然后提供给一般的非程序员公众下载。

在这个练习中,我使用 Angband,一个开源的(GPLv2)ASCII 地牢猎手游戏。这是一个简单的应用程序,其复杂程度刚好可以说明你在自己编译软件时需要考虑的问题。

网站 上下载其源代码。

2、展开源代码的存档文件

源代码通常是以存档的形式交付的,因为源代码通常由多个文件组成的。在与之交互之前,你必须先解压,不管它是一个 tarball、一个 zip 文件、一个 7z 文件,还是其他完全不同的东西。

$ tar --extract --file Angband-x.y.z.tar.gz

一旦解压缩后,就把目录切换到解压缩的目录,然后看一看。通常在目录的顶层有一个 README 文件。这个文件,一般会包含你需要做什么来编译代码的指导。README 通常包含关于代码的这些重要方面的信息:

  • 语言:代码使用的是什么语言(例如,C、C++、Rust、Python)。
  • 依赖性:你需要在你的系统上安装其他什么的软件,以便这个应用程序能够构建和运行。
  • 说明:你构建该软件所需要采取的明确步骤。偶尔,他们会在一个专门的文件中包含这些信息,这个文件被直观地称为 INSTALL

如果 README 文件中不包含这些信息,可以考虑向开发者提交一份错误报告。你不是唯一需要介绍一下源代码的人。不管他们有多么丰富的经验,每个人都会对从未见过的源代码感到陌生,而文档是很重要的!

Angband 的维护者给出了在线说明的链接,描述了如何编译代码。这份文件还描述了你需要安装哪些其他软件,尽管它并没有确切地说明这一点。该网站说,“有几个不同的可选构建的前端(GCU、SDL、SDL2 和 X11),你可以使用诸如 --enable-sdl--disable-x11 的参数配置。”这可能对你来说看起来像天书,但你经常编译代码后就会习惯。无论你是否理解 X11 或 SDL2 是什么,它们都是你经过几个月定期编译代码后经常看到的要求。你会对大多数软件需要其他软件库的想法感到适应,因为它们建立在其他技术之上。不过在这种情况下,Angband 非常灵活,无论是否有这些可选的依赖,都可以进行编译,所以现在,你可以假装没有额外的依赖。

3、编译代码

构建代码的典型步骤是:

$ ./configure
$ make
$ sudo make install

这些是使用 Autotools 构建的项目的步骤,该框架是为了规范源代码的交付方式而创建的。然而,还有一些其他框架(如 Cmake),它们需要不同的步骤。当项目没有遵循 Autotools 或 Cmake 框架时,它们往往会在 README 文件中提醒你。

配置

Angband 使用 Autotools,所以现在是编译代码的时候了!

在 Angband 目录中,首先,运行随源码一起提供的配置脚本:

$ ./configure

这一步将扫描你的系统,找到 Angband 正确构建所需的依赖性。有些依赖是非常基本的,没有它们你的电脑就无法运行,而有些则是专门的。在这一过程结束时,该脚本会给你一份关于它所发现的东西的报告:

[...]
configure: creating ./config.status
config.status: creating mk/buildsys.mk
config.status: creating mk/extra.mk
config.status: creating src/autoconf.h

Configuration:

  Install path:    /usr/local
  binary path:     /usr/local/games
  config path:     /usr/local/etc/angband/
  lib path:        /usr/local/share/angband/
  doc path:        /usr/local/share/doc/angband/
  var path:        (not used)
  (save and score files in ~/.angband/Angband/)

-- Frontends --
- Curses            Yes
- X11               Yes
- SDL2              Disabled
- SDL               Disabled
- Windows           Disabled
- Test              No
- Stats             No
- Spoilers          Yes

- SDL2 sound        Disabled
- SDL sound         Disabled

有些输出可能对你有意义,有些可能没有。无论如何,你可能注意到 SDL2 和 SDL 被标记为 “Disabled”,Test 和 Stats 都被标记为 “None”。虽然这些信息是负面的,但这并不一定是一件坏事。从本质上讲,这就是警告错误之间的区别。如果配置脚本遇到了会阻止它构建代码的东西,它就会用一个错误来提醒你。

如果你想稍微优化一下你的构建,你可以选择解决这些负面信息。通过搜索 Angband 文档,你可能会确定 Test 和 Stats 实际上并不是你感兴趣的(它们是 Angband 专用于开发者的选项)。然而,通过在线研究,你可能会发现 SDL2 将是一个很好的功能。

要解决编译代码时的依赖问题,你需要安装缺少的组件和该缺少的组件的 开发库。换句话说,Angband 需要 SDL2 来播放声音,但它需要 SDL2-devel(在 Debian 系统上称为 libsdl2-dev)来构建。用你的软件包管理器安装这两个组件:

$ sudo dnf install sdl2 sdl2-devel

再试一下配置脚本:

$ ./configure --enable-sdl2
[...]
Configuration:
[...]
- Curses                                  Yes
- X11                                     Yes
- SDL2                                    Yes
- SDL                                     Disabled
- Windows                                 Disabled
- Test                                    No
- Stats                                   No
- Spoilers                                Yes

- SDL sound                               Disabled
- SDL2 sound                              Yes

制作(编译)

一旦一切配置完毕,运行 make 命令:

$ make

这通常需要一段时间,但它提供了很多视觉反馈,所以你会知道代码正在被编译。

安装

最后一步是安装你刚刚编译的代码。安装代码并没有什么神奇之处。所做的就是复制很多文件到非常具体的目录中。无论你是从源代码编译还是运行花哨的图形安装向导,都是如此。由于这些代码会被复制到系统级目录,你必须有 root(管理)权限,这是由 sudo 命令授予的。

$ sudo make install

运行该应用程序

一旦应用程序被安装,你就可以运行它。根据 Angband 文档,启动游戏的命令是 angband,所以可以试试:

$ angband

 title=

编译代码

无论是在我的 Slackware 台式电脑上,还是在我的 CentOS 笔记本电脑上,我都会使用 NetBSD 的 pkgsrc 系统编译我自己的大部分应用程序。我发现,通过自己编译软件,我可以对应用程序中包含的功能、如何配置、使用的库版本等有自己的想法。这很有意义,它帮助我跟上了新的版本,而且因为我有时会在这个过程中发现错误,它帮助我参与了很多不同的开源项目。

你很少会只有编译软件的一种方式可选,大多数开源项目同时提供源代码(这就是为什么它被称为“开源”)和可安装包。是否从源代码编译是你自己的选择,也许是因为你想要最新版本中还没有的新功能,或者只是因为你喜欢自己编译代码。

家庭作业

Angband 可以使用 Autotools 或 Cmake,所以如果你想体验另一种构建代码的方式,可以试试这个:

$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install

你也可以尝试用 LLVM 编译器而不是 GNU 编译器集合(GCC)进行编译。现在,我把这个问题留给你自己去研究(提示:尝试设置 CC 环境变量)。

一旦你完成了对 Angband 的源代码和至少几个地牢的探索(你已经赢得了一些休息时间),可以看看其他一些代码库。很多人都会使用 Autotools 或 Cmake,而其他人可能会使用不同的东西。看看你能构建的成果!


via: https://opensource.com/article/21/11/compiling-code

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

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

这是一篇快速提示,旨在给 Ubuntu 的新用户解释构建基础包是什么、它的用处和安装步骤。

在 Ubuntu 中安装构建基础包(build-essential),只需要在终端中简单输入这个命令:

sudo apt update && sudo apt install build-essential

但围绕它有几个问题,你可能想知道答案:

  • 什么是构建基础包?
  • 它包含什么内容?
  • 为什么要安装它(如果安装的话)?
  • 如何安装它?
  • 如何删除它?

什么是 Ubuntu 中的构建基础包?

构建基础包(build-essential)实际上是属于 Debian 的。在它里面其实并不是一个软件。它包含了创建一个 Debian 包(.deb)所需的软件包列表。这些软件包包括 libcgccg++makedpkg-dev 等。构建基础包包含这些所需的软件包作为依赖,所以当你安装它时,你只需一个命令就能安装所有这些软件包。

请不要认为构建基础包是一个可以在一个命令中神奇地安装从 Ruby 到 Go 的所有开发工具的超级软件包。它包含一些开发工具,但不是全部。

你为什么要安装构建基础包?

它用来从应用的源代码创建 DEB 包。一个普通用户不会每天都去创建 DEB 包,对吗?

然而,有些用户可能会使用他们的 Ubuntu Linux 系统进行软件开发。如果你想 在 Ubuntu 中运行 c 程序,你需要 gcc 编译器。如果你想 在 Ubuntu 中运行 C++ 程序,你需要 g++ 编译器。如果你要使用一个不寻常的、只能从源代码中获得的软件,你的系统会抛出 “make 命令未找到的错误”,因为你需要先安装 make 工具。

当然,所有这些都可以单独安装。然而,利用构建基础包的优势,一次性安装所有这些开发工具要容易得多。这就是你得到的好处。

这就像 ubuntu-restricted-extras 包允许你一次安装几个媒体编解码器

现在你知道了这个包的好处,让我们看看如何安装它。

在 Ubuntu Linux 中安装构建基础包

在 Ubuntu 中按 Ctrl+Alt+T 快捷键打开终端,输入以下命令:

sudo apt update

使用 sudo 命令,你会被要求输入你的账户密码。当你输入时,屏幕上没有任何显示。这没问题。这在大多数 Linux 系统中都是这样的。盲打输入你的密码,然后按回车键。

apt update 命令刷新了本地软件包的缓存。这对于一个新安装的 Ubuntu 来说是必不可少的。

之后,运行下面的命令来安装构建基础包:

sudo apt install build-essential

它应该显示所有要安装的软件包。当要求确认时按 Y

等待安装完成。就好了。

从 Ubuntu 中删除构建基础包

保留这些开发工具不会损害你的系统。但如果你的磁盘空间不足,你可以考虑删除它。

在 Ubuntu 中,由于有 apt remove 命令,删除软件很容易:

sudo apt remove build-essential

运行 autoremove 命令来删除剩余的依赖包也是一个好主意:

sudo apt autoremove

你现在知道了所有关于构建基础包的基础(双关语)。请享受它吧~


via: https://itsfoss.com/build-essential-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

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

了解 Linux 如何使用库,包括静态库和动态库的差别,有助于你解决依赖问题。

 title=

Linux 从某种意义上来说就是一堆相互依赖的静态和动态库。对于 Linux 系统新手来说,库的整个处理过程简直是个迷。但对有经验的人来说,被构建进操作系统的大量共享代码对于编写新应用来说却是个优点。

为了让你熟悉这个话题,我准备了一个小巧的 应用例子 来展示在普通的 Linux 发行版(在其他操作系统上未验证)上是经常是如何处理库的。为了用这个例子来跟上这个需要动手的教程,请打开命令行输入:

$ git clone https://github.com/hANSIc99/library_sample
$ cd library_sample/
$ make
cc -c main.c -Wall -Werror
cc -c libmy_static_a.c -o libmy_static_a.o -Wall -Werror
cc -c libmy_static_b.c -o libmy_static_b.o -Wall -Werror
ar -rsv libmy_static.a libmy_static_a.o libmy_static_b.o
ar: creating libmy_static.a
a - libmy_static_a.o
a - libmy_static_b.o
cc -c -fPIC libmy_shared.c -o libmy_shared.o
cc -shared -o libmy_shared.so libmy_shared.o
$ make clean
rm *.o

当执行完这些命令,这些文件应当被添加进目录下(执行 ls 来查看):

my_app
libmy_static.a
libmy_shared.so

关于静态链接

当你的应用链接了一个静态库,这个库的代码就变成了可执行文件的一部分。这个动作只在链接过程中执行一次,这些静态库通常以 .a 扩展符结尾。

静态库是多个 目标 object 文件的 归档 archive ar)。这些目标文件通常是 ELF 格式的。ELF 是 可执行可链接格式 Executable and Linkable Format 的简写,它与多个操作系统兼容。

file 命令的输出可以告诉你静态库 libmy_static.aar 格式的归档文件类型。

$ file libmy_static.a
libmy_static.a: current ar archive

使用 ar -t,你可以看到归档文件的内部。它展示了两个目标文件:

$ ar -t libmy_static.a
libmy_static_a.o
libmy_static_b.o

你可以用 ax -x <archive-file> 命令来提取归档文件的文件。被提出的都是 ELF 格式的目标文件:

$ ar -x libmy_static.a
$ file libmy_static_a.o
libmy_static_a.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

关于动态链接

动态链接指的是使用共享库。共享库通常以 .so 的扩展名结尾(“ 共享对象 shared object ” 的简写)。

共享库是 Linux 系统中依赖管理的最常用方法。这些共享库在应用启动前被载入内存,当多个应用都需要同一个库时,这个库在系统中只会被加载一次。这个特性减少了应用的内存占用。

另外一个值得注意的地方是,当一个共享库的 bug 被修复后,所有引用了这个库的应用都会受益。但这也意味着,如果一个 bug 还没被发现,那所有相关的应用都会遭受这个 bug 影响(如果这个应用使用了受影响的部分)。

当一个应用需要某个特定版本的库,但是 链接器 linker 只知道某个不兼容版本的位置,对于初学者来说这个问题非常棘手。在这个场景下,你必须帮助链接器找到正确版本的路径。

尽管这不是一个每天都会遇到的问题,但是理解动态链接的原理总是有助于你修复类似的问题。

幸运的是,动态链接的机制其实非常简洁明了。

为了检查一个应用在启动时需要哪些库,你可以使用 ldd 命令,它会打印出给定文件所需的动态库:

$ ldd my_app
        linux-vdso.so.1 (0x00007ffd1299c000)
        libmy_shared.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f56b869b000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f56b8881000)

可以注意到 libmy_shared.so 库是代码仓库的一部分,但是没有被找到。这是因为负责在应用启动之前将所有依赖加载进内存的动态链接器没有在它搜索的标准路径下找到这个库。

对新手来说,与常用库(例如 bizp2)版本不兼容相关的问题往往十分令人困惑。一种方法是把该仓库的路径加入到环境变量 LD_LIBRARY_PATH 中来告诉链接器去哪里找到正确的版本。在本例中,正确的版本就在这个目录下,所以你可以导出它至环境变量:

$ LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH

现在动态链接器知道去哪找库了,应用也可以执行了。你可以再次执行 ldd 去调用动态链接器,它会检查应用的依赖然后加载进内存。内存地址会在对象路径后展示:

$ ldd my_app
        linux-vdso.so.1 (0x00007ffd385f7000)
        libmy_shared.so => /home/stephan/library_sample/libmy_shared.so (0x00007f3fad401000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3fad21d000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3fad408000)

想知道哪个链接器被调用了,你可以用 file 命令:

$ file my_app
my_app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=26c677b771122b4c99f0fd9ee001e6c743550fa6, for GNU/Linux 3.2.0, not stripped

链接器 /lib64/ld-linux-x86–64.so.2 是一个指向 ld-2.30.so 的软链接,它也是我的 Linux 发行版的默认链接器:

$ file /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: symbolic link to ld-2.31.so

回头看看 ldd 命令的输出,你还可以看到(在 libmy_shared.so 边上)每个依赖都以一个数字结尾(例如 /lib64/libc.so.6)。共享对象的常见命名格式为:

libXYZ.so.<MAJOR>.<MINOR>

在我的系统中,libc.so.6 也是指向同一目录下的共享对象 libc-2.31.so 的软链接。

$ file /lib64/libc.so.6
/lib64/libc.so.6: symbolic link to libc-2.31.so

如果你正在面对一个应用因为加载库的版本不对导致无法启动的问题,有很大可能你可以通过检查整理这些软链接或者确定正确的搜索路径(查看下方“动态加载器:ld.so”一节)来解决这个问题。

更为详细的信息请查看 ldd 手册页

动态加载

动态加载的意思是一个库(例如一个 .so 文件)在程序的运行时被加载。这是使用某种特定的编程方法实现的。

当一个应用使用可以在运行时改变的插件时,就会使用动态加载。

查看 dlopen 手册页 获取更多信息。

动态加载器:ld.so

在 Linux 系统中,你几乎总是正在跟共享库打交道,所以必须有个机制来检测一个应用的依赖并将其加载进内存中。

ld.so 按以下顺序在这些地方寻找共享对象:

  1. 应用的绝对路径或相对路径下(用 GCC 编译器的 -rpath 选项硬编码的)
  2. 环境变量 LD_LIBRARY_PATH
  3. /etc/ld.so.cache 文件

需要记住的是,将一个库加到系统库归档 /usr/lib64 中需要管理员权限。你可以手动拷贝 libmy_shared.so 至库归档中来让应用可以运行,而避免设置 LD_LIBRARY_PATH

unset LD_LIBRARY_PATH
sudo cp libmy_shared.so /usr/lib64/

当你运行 ldd 时,你现在可以看到归档库的路径被展示出来:

$ ldd my_app
        linux-vdso.so.1 (0x00007ffe82fab000)
        libmy_shared.so => /lib64/libmy_shared.so (0x00007f0a963e0000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f0a96216000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f0a96401000)

在编译时定制共享库

如果你想你的应用使用你的共享库,你可以在编译时指定一个绝对或相对路径。

编辑 makefile(第 10 行)然后通过 make -B 来重新编译程序。然后 ldd 输出显示 libmy_shared.so 和它的绝对路径一起被列出来了。

把这个:

CFLAGS =-Wall -Werror -Wl,-rpath,$(shell pwd)

改成这个(记得修改用户名):

CFLAGS =/home/stephan/library_sample/libmy_shared.so

然后重新编译:

$ make

确认下它正在使用你设定的绝对路径,你可以在输出的第二行看到:

$ ldd my_app
    linux-vdso.so.1 (0x00007ffe143ed000)
        libmy_shared.so => /lib64/libmy_shared.so (0x00007fe50926d000)
        /home/stephan/library_sample/libmy_shared.so (0x00007fe509268000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fe50909e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe50928e000)

这是个不错的例子,但是如果你在编写给其他人用的库,它是怎样工作的呢?新库的路径可以通过写入 /etc/ld.so.conf 或是在 /etc/ld.so.conf.d/ 目录下创建一个包含路径的 <library-name>.conf 文件来注册至系统。之后,你必须执行 ldconfig 命令来覆写 ld.so.cache 文件。这一步有时候在你装了携带特殊的共享库的程序来说是不可省略的。

查看 ld.so 的手册页 获取更多详细信息。

怎样处理多种架构

通常来说,32 位和 64 位版本的应用有不同的库。下面列表展示了不同 Linux 发行版库的标准路径:

红帽家族

  • 32 位:/usr/lib
  • 64 位:/usr/lib64

Debian 家族

  • 32 位:/usr/lib/i386-linux-gnu
  • 64 位:/usr/lib/x86_64-linux-gnu

Arch Linux 家族

  • 32 位:/usr/lib32
  • 64 位:/usr/lib64

FreeBSD(技术上来说不算 Linux 发行版)

  • 32 位:/usr/lib32
  • 64 位:/usr/lib

知道去哪找这些关键库可以让库链接失效的问题成为历史。

虽然刚开始会有点困惑,但是理解 Linux 库的依赖管理是一种对操作系统掌控感的表现。在其他应用程序中运行这些步骤,以熟悉常见的库,然后继续学习怎样解决任何你可能遇到的库的挑战。


via: https://opensource.com/article/20/6/linux-libraries

作者:Stephan Avenwedde 选题:lujun9972 译者:tt67wq 校对:wxy

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