前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Linux】《how linux work》第十六章 从 C 源代码编译软件入门

【Linux】《how linux work》第十六章 从 C 源代码编译软件入门

原创
作者头像
阿东
发布2024-05-06 11:08:31
720
发布2024-05-06 11:08:31
举报
文章被收录于专栏:《How Linux Work》《How Linux Work》

第 16 章 Introduction to Compiling Software From C Source Code(从 C 源代码编译软件入门)

Most nonproprietary third-party Unix software packages come as source code that you can build and install. One reason for this is that Unix (and Linux itself) has so many different flavors and architectures that it would be difficult to distribute binary packages for all possible platform combinations. The other reason, which is at least as important, is that widespread source code distribution throughout the Unix community encourages users to contribute bug fixes and new features to software, giving meaning to the term open source.

大多数非专有的第三方Unix软件包都是以源代码的形式提供的,您可以构建和安装。

这样做的一个原因是Unix(包括Linux本身)有很多不同的版本和架构,难以为所有可能的平台组合分发二进制包。

另一个至少同等重要的原因是,在整个Unix社区广泛分发源代码鼓励用户为软件贡献错误修复和新功能,赋予了开源这个词以意义。

You can get nearly everything you see on a Linux system as source code—from the kernel and C library to the web browsers. It’s even possible to update and augment your entire system by (re-)installing parts of your system from the source code. However, you probably shouldn’t update your machine by installing everything from source code, unless you really enjoy the process or have some other reason

您几乎可以从Linux系统中获取您看到的所有东西的源代码-从内核和C库到Web浏览器。

甚至可以通过(重新)安装系统的某些部分来更新和增强整个系统。

但是,除非您真的喜欢这个过程或有其他原因,否则您可能不应该通过安装所有源代码来更新您的计算机。

Linux distributions typically provide easier ways to update core parts of the system, such as the programs in /bin, and one particularly important property of distributions is that they usually fix security problems very quickly. But don’t expect your distribution to provide everything for you. Here are some reasons why you may want to install certain packages yourself:

Linux发行版通常提供了更简单的方法来更新系统的核心部分,例如/bin中的程序,发行版的一个特别重要的特性是它们通常非常快速地修复安全问题。

但是不要期望您的发行版为您提供一切。以下是您可能希望自己安装某些软件包的原因:

o To control configuration options.

o To install the software anywhere you like. You can even install several different versions of the same package.

o To control the version that you install. Distributions don’t always stay up-to-date with the latest versions of all packages, particularly add-ons to software packages (such as Python libraries).

o To better understand how a package works.

  • 控制配置选项。
  • 在任何您喜欢的位置安装软件。您甚至可以安装同一个软件包的几个不同版本。
  • 控制您安装的版本。发行版并不总是与所有软件包的最新版本保持同步,特别是软件包的附加组件(如Python库)。
  • 更好地理解软件包的工作原理。16.1 Software Build Systems

There are many programming environments on Linux, from traditional C to interpreted scripting languages such as Python. Each typically has at least one distinct system for building and installing packages in addition to the tools that a Linux distribution provides.

在Linux上有许多编程环境,从传统的C语言到解释型脚本语言如Python。

每种环境通常至少有一个独特的系统用于构建和安装软件包,除了Linux发行版提供的工具。

We’re going to look at compiling and installing C source code in this chapter with only one of these build systems—the configuration scripts generated from the GNU autotools suite. This system is generally considered stable, and many of the basic Linux utilities use it. Because it’s based on existing tools such as make, after you see it in action, you’ll be able to transfer your knowledge to other build systems.

在本章中,我们将着眼于编译和安装C源代码,只使用这些构建系统中的一个——从GNU autotools套件生成的配置脚本。

这个系统通常被认为是稳定的,许多基本的Linux工具都在使用它。

因为它基于现有的工具如make,一旦你看到它的运行方式,你就能将你的知识转移到其他构建系统上。

Installing a package from C source code usually involves the following steps:

从C源代码安装软件包通常包括以下步骤:

  1. Unpack the source code archive.
  2. Configure the package.
  3. Run make to build the programs.
  4. Run make install or a distribution-specific install command to install the package.
  5. 解压源代码存档。
  6. 配置软件包。
  7. 运行make来构建程序。
  8. 运行make install或特定于发行版的安装命令来安装软件包。

NOTE You should understand the basics in Chapter 15 before proceeding with this chapter.注意 在继续本章之前,您应该先了解第15章的基础知识。

16.2 Unpacking C Source Packages(解压 C 源代码包)

A package’s source code distribution usually comes as a .tar.gz, .tar.bz2, or .tar.xz file, and you should unpack the file as described in 2.18 Archiving and Compressing Files. Before you unpack, though, verify the contents of the archive with tar tvf or tar ztvf, because some packages don’t create their own subdirectories in the directory where you extract the archive.

一个软件包的源代码分发通常以 .tar.gz、.tar.bz2 或 .tar.xz 文件的形式出现,你应该按照 2.18 节中描述的方法解压文件。

在解压之前,使用 tar tvf 或 tar ztvf 命令验证归档文件的内容,因为有些软件包在你解压归档文件的目录中不会创建自己的子目录。

Output like this means that the package is probably okay to unpack:

像下面这样的输出意味着这个软件包可能可以安全解压:

代码语言:sh
复制
package-1.23/Makefile.in 
package-1.23/README 
package-1.23/main.c 
package-1.23/bar.c 
--snip-- 

However, you may find that not all files are in a common directory (like package-1.23 in the preceding example):

然而,你可能会发现并非所有文件都在一个共同的目录中(就像前面的示例中的 package-1.23 一样):

代码语言:sh
复制
Makefile 
README 
main.c 
--snip-- 

Extracting an archive like this one can leave a big mess in your current directory. To avoid that, create a new directory and cd there before extracting the contents of the archive. Finally, beware of packages that contain files with absolute pathnames like this:

解压这样一个归档文件可能会在当前目录留下一团糟。

为了避免这种情况,应该先创建一个新目录并在解压归档文件内容之前进入该目录。

最后,要注意那些包含绝对路径文件的软件包,比如:

代码语言:sh
复制
/etc/passwd 
/etc/inetd.conf 

You likely won’t come across anything like this, but if you do, remove the archive from your system. It probably contains a Trojan horse or some other malicious code.

你可能不太可能遇到类似的情况,但如果真的遇到了,应该将该归档文件从系统中删除。

它很可能包含了特洛伊木马或其他恶意代码。

16.2.1 Where to Start

Once you’ve extracted the contents of a source archive and have a bunch of files in front of you, try to get a feel for the package. In particular, look for the files README and INSTALL. Always look at any README files first because they often contain a description of the package, a small manual, installation hints, and other useful information. Many packages also come with INSTALL files with instructions on how to compile and install the package. Pay particular attention to special compiler options and definitions.

一旦您提取了源代码归档文件的内容,面前摆放着一堆文件时,试着了解一下这个软件包。

特别是要查找 README 和 INSTALL 文件。

始终首先查看任何 README 文件,因为它们通常包含软件包的描述、简要手册、安装提示以及其他有用信息。

许多软件包还附带有包含如何编译和安装软件包的说明的 INSTALL 文件。特别要注意特殊的编译选项和定义。

In addition to README and INSTALL files, you will find other package files that roughly fall into three categories:

除了 README 和 INSTALL 文件之外,您还会找到其他大致分为三类的软件包文件:

o Files relating to the make system, such as Makefile, Makefile.in, configure, and CMakeLists.txt. Some very old packages come with a Makefile that you may need to modify, but most use a configuration utility such as GNU autoconf or CMake. They come with a script or configuration file (such as configure or CMakeLists.txt) to help generate a Makefile from Makefile.in based on your system settings and configuration options.

  • 与构建系统相关的文件,如 Makefile、Makefile.in、configure 和 CMakeLists.txt。
  • 一些非常古老的软件包带有一个 Makefile,您可能需要修改它,但大多数使用 GNU autoconf 或 CMake 等配置实用程序。
  • 它们带有一个脚本或配置文件(如 configure 或 CMakeLists.txt),以帮助根据您的系统设置和配置选项从 Makefile.in 生成 Makefile。

o Source code files ending in .c, .h, or .cc. C source code files may appear just about anywhere in a package directory. C++ source code files usually have .cc, .C, or .cxx suffixes.

o Object files ending in .o or binaries. Normally, there aren’t any object files in source code distributions, but you might find some in rare cases when the package maintainer is not permitted to release certain source code and you need to do something special in order to use the object files. In most cases, object (or binary executable) files in a source distribution mean that the package wasn’t put together well, and you should run make clean to make sure that you get a fresh compile.

目标文件以.o或二进制文件结尾。

通常,在源代码分发中不会有任何目标文件,但在罕见情况下,当软件包维护者无权发布某些源代码时,您可能会发现一些目标文件,这时您需要采取一些特殊措施才能使用这些目标文件。

在大多数情况下,在源代码分发中的目标(或二进制可执行)文件意味着软件包组装不完整,您应该运行make clean以确保进行新的编译。

16.3 GNU Autoconf

Even though C source code is usually fairly portable, differences on each platform make it impossible to compile most packages with a single Makefile. Early solutions to this problem were to provide individual Makefiles for every operating system or to provide a Makefile that was easy to modify. This approach evolved into scripts that generate Makefiles based on an analysis of the system used to build the package.

尽管C源代码通常是相当可移植的,但每个平台上的差异使得几乎不可能使用单个Makefile编译大多数软件包。

早期解决这个问题的方法是为每个操作系统提供单独的Makefile,或者提供一个易于修改的Makefile。

这种方法演变成了基于对用于构建软件包的系统的分析而生成Makefile的脚本。

GNU autoconf is a popular system for automatic Makefile generation. Packages using this system come with files named configure, Makefile.in, and config.h.in. The .in files are templates; the idea is to run the configure script in order to discover the characteristics of your system, then make substitutions in the .in files to create the real build files. For the end user, it’s easy; to generate a Makefile from Makefile.in, run configure:

GNU Autoconf 是一个用于自动生成Makefile的流行系统。

使用该系统的软件包附带名为configure、Makefile.in和config.h.in的文件。

这些 .in 文件是模板;其思想是运行 configure 脚本以发现您系统的特征,然后在 .in 文件中进行替换以创建真正的构建文件。

对于最终用户来说,这很简单;要从 Makefile.in 生成 Makefile,只需运行 configure 命令:

代码语言:sh
复制
$ ./configure 

You should get a lot of diagnostic output as the script checks your system for prerequisites. If all goes well, configure creates one or more Makefiles and a config.h file, as well as a cache file (config.cache), so that it doesn’t need to run certain tests again.

在脚本检查系统先决条件时,您应该会得到大量的诊断输出。

如果一切顺利,configure 将创建一个或多个 Makefile 和一个 config.h 文件,以及一个缓存文件(config.cache),这样它就不需要再次运行某些测试。

Now you can run make to compile the package. A successful configure step doesn’t necessarily mean that the make step will work, but the chances are pretty good. (See 16.6 Troubleshooting Compiles and Installations for troubleshooting failed configures and compiles.)

现在,您可以运行 make 来编译软件包。

成功的 configure 步骤并不一定意味着 make 步骤会成功,但成功的可能性很大。

(请参阅16.6 故障排除编译和安装以了解有关故障排除失败的 configure 和编译的信息。)

Let’s get some firsthand experience with the process.

NOTE At this point, you must have all of the required build tools available on your system. For Debian and Ubuntu, the easiest way is to install the build-essential package; in Fedora-like systems, use the Chapter 15 groupinstall.

让我们亲身体验一下这个过程。

注意:在这一点上,您必须在系统上具有所有所需的构建工具。

对于 Debian 和 Ubuntu,最简单的方法是安装 build-essential 软件包;对于类似 Fedora 的系统,请使用第 15 章的 groupinstall。

16.3.1 An Autoconf Example(Autoconf 示例)

Before discussing how you can change the behavior of autoconf, let’s look at a simple example so that you know what to expect. You’ll install the GNU coreutils package in your own home directory (to make sure that you don’t mess up your system). Get the package from http://ftp.gnu.org/gnu/coreutils/ (the latest version is usually the best), unpack it, change to its directory, and configure it like this:

在讨论如何更改autoconf的行为之前,让我们看一个简单的示例,以便您知道可以期待什么。

您将在自己的主目录中安装GNU coreutils软件包(以确保不会搞乱您的系统)。

http://ftp.gnu.org/gnu/coreutils/获取软件包(通常最新版本是最好的),解压缩,进入其目录,并像这样配置它:

代码语言:sh
复制
$ ./configure --prefix=$HOME/mycoreutils
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
--snip--
config.status: executing po-directories commands
config.status: creating po/POTFILES
config.status: creating po/Makefile

Now run make:

现在运行 make 命令:

代码语言:sh
复制
$ make
 GEN lib/alloca.h
 GEN lib/c++defs.h
--snip--
make[2]: Leaving directory '/home/juser/coreutils-8.22/gnulib-tests'
make[1]: Leaving directory '/home/juser/coreutils-8.22'

Next, try to run one of the executables that you just created, such as ./src/ls, and try running make check to run a series of tests on the package. (This might take a while, but it’s interesting to see.)

接下来,尝试运行你刚刚创建的可执行文件之一,比如 ./src/ls,然后尝试运行 make check 来对该软件包运行一系列测试。

(可能需要一段时间,但很有趣。)

Finally, you’re ready to install the package. Do a dry run with make -n first to see what make install does without actually doing the install:

最后,你已经准备好安装该软件包了。首先通过 make -n 命令进行干跑,查看 make install 实际执行的操作:

代码语言:sh
复制
$ make -n install 

Browse through the output, and if nothing seems strange (such as installing anywhere other than your mycoreutils directory), do the install for real:

浏览输出内容,如果没有发现异常(比如安装到非 mycoreutils 目录之外),那么可以进行实际安装:

代码语言:sh
复制
$ make install 

You should now have a subdirectory named mycoreutils in your home directory that contains bin, share, and other subdirectories. Check out some of the programs in bin (you just built many of the basic tools that you learned in Chapter 2). Finally, because you configured the mycoreutils directory to be independent of the rest of your system, you can remove it completely without worrying about causing damage.

现在,你的主目录下应该有一个名为 mycoreutils 的子目录,其中包含 bin、share 和其他子目录。查看 bin 目录中的一些程序(你刚刚构建了第二章中学到的许多基本工具)。

最后,因为你已经配置了 mycoreutils 目录,使其独立于系统的其他部分,所以可以完全删除它,而不必担心造成损坏。

16.3.2 Installing Using a Packaging Tool(使用包管理工具进行安装)

On most distributions, it’s possible to install new software as a package that you can maintain later with your distribution’s packaging tools. Debian-based distributions such as Ubuntu are perhaps the easiest; rather than running a plain make install, you can do it with the checkinstall utility, as follows:

在大多数发行版中,可以将新软件安装为一个包,之后可以使用发行版的包管理工具进行维护。

基于Debian的发行版如Ubuntu可能是最简单的;你可以使用checkinstall工具来代替简单的make install,具体操作如下:

代码语言:sh
复制
# checkinstall make install

Use the --pkgname=name option to give your new package a specific name.

使用 --pkgname=name 选项为你的新包指定一个特定的名称。

Creating an RPM package is a little more involved, because you must first create a directory tree for your package(s). You can do this with the rpmdev-setuptree command; when complete, you can use the rpmbuild utility to work through the rest of the steps. It’s best to follow an online tutorial for this process.

创建一个RPM包涉及的步骤略微复杂,因为你必须首先为你的包创建一个目录结构。

你可以使用rpmdev-setuptree命令来完成这一步;完成后,你可以使用rpmbuild工具来完成剩余的步骤。

最好是按照在线教程来进行这个过程。

16.3.3 configure Script Options

You’ve just seen one of the most useful options for the configure script: using --prefix to specify the installation directory. By default, the install target from an autoconf-generated Makefile uses a prefix of /usr/local—that is, binary programs go in /usr/local/bin, libraries go in /usr/local/lib, and so on. You will often want to change that prefix like this:

你刚刚看到了配置脚本中最有用的选项之一:使用--prefix来指定安装目录。

默认情况下,从autoconf生成的Makefile的安装目标使用的是/usr/local作为前缀,也就是说,二进制程序会被安装在/usr/local/bin目录下,库会被安装在/usr/local/lib目录下,依此类推。

通常情况下,你会想要修改这个前缀,像这样:

代码语言:sh
复制
$ ./configure --prefix=new_prefix 

Most versions of configure have a --help option that lists other configuration options. Unfortunately, the list is usually so long that it’s sometimes hard to figure out what might be important, so here are some essential options: o --bindir=directory Installs executables in directory.

大多数版本的configure都有一个--help选项,列出了其他配置选项。

不过,这个列表通常很长,有时很难弄清哪些选项可能是重要的,因此这里列出了一些关键选项:

o --sbindir=directory Installs system executables in directory.

o --libdir=directory Installs libraries in directory.

o --disable-shared Prevents the package from building shared libraries. Depending on the library, this can save hassles later on (see 15.1.4 Shared Libraries).

o --with-package=directory Tells configure that package is in directory. This is handy when a necessary library is in a nonstandard location. Unfortunately, not all configure scripts recognize this type of option, and it can be difficult to determine the exact syntax.

  • --bindir=directory:将可执行文件安装在指定目录中。
  • --sbindir=directory:将系统可执行文件安装在指定目录中。
  • --libdir=directory:将库文件安装在指定目录中。
  • --disable-shared:阻止软件包构建共享库。 - 根据库的不同,这样做可以在以后避免麻烦(参见15.1.4 共享库)。
  • --with-package=directory:告诉configure软件包在指定目录中。 - 当一个必要的库位于非标准位置时,这会很方便。 - 不过,并非所有的configure脚本都能识别这种类型的选项,而且确切的语法可能很难确定。Using Separate Build Directories(使用单独的构建目录)

You can create separate build directories if you want to experiment with some of these options. To do so, create a new directory anywhere on the system and, from that directory, run the configure script in the original package source code directory. You’ll find that configure then makes a symbolic link farm in your new build directory, where all of the links point back to the source tree in the original package directory. (Some developers prefer that you build packages this way, because the original source tree is never modified. This is also useful if you want to build for more than one platform or configuration option set using the same source package.)

如果你想尝试其中一些选项,可以创建单独的构建目录。

要这样做,可以在系统的任何位置创建一个新目录,然后在该目录中运行原始软件包源代码目录中的 configure 脚本。

你会发现,configure 然后会在你的新构建目录中创建一个符号链接集,所有的链接都指向原始软件包目录中的源代码树。

(一些开发人员更喜欢以这种方式构建软件包,因为原始源代码树永远不会被修改。

如果你想使用同一个源代码包构建多个平台或配置选项集,这也很有用。)

16.3.4 Environment Variables(环境变量)

You can influence configure with environment variables that the configure script puts into make variables. The most important ones are CPPFLAGS, CFLAGS, and LDFLAGS. But be aware that configure can be very picky about environment variables. For example, you should normally use CPPFLAGS instead of CFLAGS for header file directories, because configure often runs the preprocessor independently of the compiler.

你可以通过环境变量影响 configure,这些环境变量会被 configure 脚本放入 make 变量中。

其中最重要的是 CPPFLAGS、CFLAGS 和 LDFLAGS。

但要注意,configure 对环境变量可能会非常挑剔。

例如,通常应该使用 CPPFLAGS 而不是 CFLAGS 来指定头文件目录,因为 configure 经常会独立于编译器运行预处理器。

In bash, the easiest way to send an environment variable to configure is by placing the variable assignment in front of ./configure on the command line. For example, to define a DEBUG macro for the preprocessor, use this command:

在 bash 中,将环境变量赋值放在命令行中 ./configure 的前面是将环境变量传递给 configure 的最简单方法。

例如,要为预处理器定义一个 DEBUG 宏,可以使用以下命令:

代码语言:sh
复制
 $ CPPFLAGS=-DDEBUG ./configure 

NOTE You can also pass a variable as an option to configure; for example:

注意:您也可以将变量作为选项传递以进行配置;例如:

代码语言:sh
复制
 $ ./configure CPPFLAGS=-DDEBUG 

Environment variables are especially handy when configure doesn’t know where to look for third-party include files and libraries. For example, to make the preprocessor search in include_dir, run this command:

当configure不知道在哪里查找第三方包含文件和库时,环境变量尤其方便。

例如,要使预处理器在include_dir中搜索,运行以下命令:

代码语言:sh
复制
 $ CPPFLAGS=-Iinclude_dir ./configure 

As shown in 15.2.6 Standard Macros and Variables, to make the linker look in lib_dir, use this command:

如15.2.6标准宏和变量中所示,要使链接器在lib_dir中查找,使用以下命令:

代码语言:sh
复制
 $ LDFLAGS=-Llib_dir ./configure 

If lib_dir has shared libraries (see 15.1.4 Shared Libraries), the previous command probably won’t set the runtime dynamic linker path. In that case, use the -rpath linker option in addition to -L:

如果lib_dir中有共享库(参见15.1.4共享库),上述命令可能不会设置运行时动态链接器路径。

在这种情况下,除了-L之外,还要使用-rpath链接器选项:

代码语言:sh
复制
 $ LDFLAGS="-Llib_dir -Wl,-rpath=lib_dir" ./configure 

Be careful when setting variables. A small slip can trip up the compiler and cause configure to fail. For example, say you forget the - in -I, as shown here:

设置变量时要小心。

一个小错误可能导致编译器出错并导致configure失败。

例如,假设您忘记了-I中的减号,如下所示:

代码语言:sh
复制
 $ CPPFLAGS=Iinclude_dir ./configure 

This yields an error like this:

这将导致如下错误:

代码语言:sh
复制
 configure: error: C compiler cannot create executables 
 See 'config.log' for more details 

Digging through the config.log generated from this failed attempt yields this:

从这次失败尝试生成的config.log中查看,会发现如下内容:

代码语言:sh
复制
 configure:5037: checking whether the C compiler works
configure:5059: gcc Iinclude_dir conftest.c >&5
gcc: error: Iinclude_dir: No such file or directory
configure:5063: $? = 1
configure:5101: result: no

16.3.5 Autoconf Targets ( Autoconf 目标 )

Once you get configure working, you’ll find that the Makefile that it generates has a number of other useful targets in addition to the standard all and install:

一旦您让 configure 正常工作,您会发现它生成的 Makefile 除了标准的 all 和 install 之外,还有许多其他有用的目标:

o make clean As described in Chapter 15, this removes all object files, executables, and libraries.

o make distclean This is similar to make clean except that it removes all automatically generated files, including Makefiles, config.h, config.log, and so on. The idea is that the source tree should look like a newly unpacked distribution after running make distclean.

o make check Some packages come with a battery of tests to verify that the compiled programs work properly; the command make check runs the tests.

o make install-strip This is like make install except that it strips the symbol table and other debugging information from executables and libraries when installing. Stripped binaries require much less space.

  • make clean 如第15章所述,这将删除所有目标文件、可执行文件和库文件。
  • make distclean 这类似于 make clean,但它会删除所有自动生成的文件,包括 Makefiles、config.h、config.log 等。其思想是运行 make distclean 后源代码树应该看起来像是新解压的发行版。
  • make check 一些软件包附带一系列测试来验证编译后的程序是否正常工作;make check 命令会运行这些测试。
  • make install-strip 这类似于 make install,但在安装时会从可执行文件和库文件中剥离符号表和其他调试信息。剥离后的二进制文件占用的空间要少得多。

16.3.6 Autoconf Log Files (Autoconf 日志文件)

If something goes wrong during the configure process and the cause isn’t obvious, you can examine config.log to find the problem. Unfortunately, config.log is often a gigantic file, which can make it difficult to locate the exact source of the problem.

如果在配置过程中出现问题且原因不明显,您可以查看 config.log 文件找出问题所在。

不幸的是,config.log 通常是一个庞大的文件,这可能会使定位问题的确切源头变得困难。

The general approach to finding the problem is to go to the very end of config.log (for example, by pressing G in less) and then page back up until you see the problem. However, there is still a lot of stuff at the end because configure dumps its entire environment there, including output variables, cache variables, and other definitions. So rather than going to the end and paging up, go to the end and search backward for a string such as for more details or some other part near the end of the failed configure output. (Remember that you can initiate a reverse search in less with the ? command.) There’s a good chance that the error will be just above what your search finds.

找出问题的一般方法是转到 config.log 的末尾(例如,通过在 less 中按 G 键),然后向上翻页,直到找到问题所在。

然而,末尾仍然有很多内容,因为 configure 在那里转储了整个环境,包括输出变量、缓存变量和其他定义。

因此,与其转到末尾再向上翻页,不如转到末尾并向后搜索一个字符串,比如“for more details”或者失败的 configure 输出末尾附近的其他部分。

(请记住,您可以使用 less 中的 ? 命令进行反向搜索。)很可能错误就在您搜索到的位置的正上方。

16.3.7 pkg-config

There are so many third-party libraries that keeping all of them in a common location can be messy. However, installing each with a separate prefix can lead to problems when building packages that require these thirdparty libraries. For example, if you want to compile OpenSSH, you need the OpenSSL library. How do you tell the OpenSSH configuration process the location of the OpenSSL libraries and which libraries are required? Many libraries now use the pkg-config program not only to advertise the locations of their include files and libraries but also to specify the exact flags that you need to compile and link a program. The syntax is as follows:

有太多第三方库,将它们都放在一个共同的位置可能会很混乱。

然而,为每个库单独安装一个前缀可能会导致在构建需要这些第三方库的软件包时出现问题。

例如,如果你想编译 OpenSSH,你需要 OpenSSL 库。

你如何告诉 OpenSSH 配置过程 OpenSSL 库的位置以及需要哪些库?

许多库现在使用 pkg-config 程序,不仅用于广告它们的头文件和库的位置,还用于指定编译和链接程序所需的确切标志。

语法如下:

代码语言:sh
复制
$ pkg-config options package1 package2 ... 

For example, to find the libraries required for OpenSSL, you can run this command:

例如,要查找 OpenSSL 所需的库,你可以运行这个命令:

代码语言:sh
复制
$ pkg-config --libs openssl 

The output should be something like this:

输出应该类似于:

代码语言:sh
复制
-lssl -lcrypto 

To see all libraries that pkg-config knows about, run this command:

要查看 pkg-config 知道的所有库,运行这个命令:

代码语言:sh
复制
$ pkg-config --list-all 

How pkg-config Works (pkg-config的工作原理)

If you look behind the scenes, you will find that pkg-config finds package information by reading configuration files that end with .pc. For example, here is openssl.pc for the OpenSSL socket library, as seen on an Ubuntu system (located in /usr/lib/i386-linux-gnu/pkgconfig):

如果你深入了解,你会发现pkg-config通过读取以.pc结尾的配置文件来获取软件包信息。

例如,这里是OpenSSL套接字库的openssl.pc文件,可以在Ubuntu系统中找到(位于/usr/lib/i386-linux-gnu/pkgconfig目录下):

代码语言:sh
复制
prefix=/usr
exec_prefix=${prefix}
libdir=${exec_prefix}/lib/i386-linux-gnu
includedir=${prefix}/include
Name: OpenSSL
Description: Secure Sockets Layer and cryptography libraries and tools
Version: 1.0.1
Requires:
Libs: -L${libdir} -lssl -lcrypto
Libs.private: -ldl -lz
Cflags: -I${includedir} exec_prefix=${prefix}

You can change this file, for example, by adding -Wl,-rpath=${libdir} to the library flags to set a runtime dynamic linker path. However, the bigger question is how pkg-config finds the .pc files in the first place. By default, pkg-config looks in the lib/pkgconfig directory of its installation prefix. For example, a pkg-config installed with a /usr/local prefix looks in /usr/local/lib/ pkgconfig.

你可以修改这个文件,例如,通过在库标志中添加-Wl,-rpath=${libdir}来设置运行时动态链接器路径。

然而,更重要的问题是pkg-config如何首次找到.pc文件。

默认情况下,pkg-config在其装前缀的lib/pkgconfig目录中查找。

例如,使用/usr/local前缀安装的pkg-config会在/usr/local/lib/pkgconfig目录中查找。

Installing pkg-config Files in Nonstandard Locations

Unfortunately, by default, pkg-config does not read any .pc files outside its installation prefix. So a .pc file that’s in a nonstandard location, such as /opt/ openssl/lib/pkgconfig/openssl.pc, will be out of the reach of any stock pkg-config installation. There are two basic ways to make .pc files available outside of the pkg- config installation prefix:

不幸的是,默认情况下,pkg-config不会读取安装前缀之外的任何.pc文件。

因此,位于非标准位置(例如/opt/openssl/lib/pkgconfig/openssl.pc)的.pc文件将无法被任何标准的pkg-config安装所找到。

有两种基本方法可以让.pc文件在pkg-config安装前缀之外的位置可用:

o Make symbolic links (or copies) from the actual .pc files to the central pkgconfig directory.

o Set your PKG_CONFIG_PATH environment variable to include any extra pkgconfig directories. This strategy does not work well on a system-wide basis.

  • 从实际的.pc文件创建符号链接(或复制)到中央pkgconfig目录。
  • 设置PKG_CONFIG_PATH环境变量以包括任何额外的pkgconfig目录。
  • 这种策略在系统范围内效果不佳。16.4 Installation Practice(安装实践)

Knowing how to build and install software is good, but knowing when and where to install your own packages is even more useful. Linux distributions try to cram in as much software as possible at installation, and you should always check whether it would be best to install a package yourself instead. Here are the advantages of doing installs on your own:

知道如何构建和安装软件是很好的,但知道何时何地安装您自己的软件包更加有用。

Linux 发行版在安装时尝试尽可能多地塞入软件,您应该始终检查是否最好自己安装软件包。

以下是自行安装的优点:

o You can customize package defaults.

o When installing a package, you often get a clearer picture of how to use the package.

o You control the release that you run. o It’s easier to back up a custom package.

o It’s easier to distribute self-installed packages across a network (as long as the architecture is consistent and the installation location is relatively isolated).

Here are the disadvantages:

o It takes time.

o Custom packages do not automatically upgrade themselves. Distributions keep most packages up-to-date without requiring much work. This is a particular concern for packages that interact with the network, because you want to ensure that you always have the latest security updates.

o If you don’t actually use the package, you’re wasting your time.

o There is a potential for misconfiguring packages.

  • 您可以自定义软件包默认设置。
  • 安装软件包时,通常能更清楚地了解如何使用该软件包。
  • 您控制所运行的版本。
  • 更容易备份定制软件包。
  • 更容易在网络中分发自行安装的软件包(只要架构一致且安装位置相对隔离)。

以下是缺点:

  • 需要时间。
  • 定制软件包不会自动升级。发行版会保持大多数软件包保持最新,而无需太多工作。这对与网络交互的软件包尤为重要,因为您希望始终拥有最新的安全更新。
  • 如果您实际上不使用该软件包,则是在浪费时间。
  • 存在错误配置软件包的潜在风险。

There’s not much point in installing packages such as the ones in the coreutils package that you built earlier in the chapter (ls, cat, and so on) unless you’re building a very custom system. On the other hand, if you have a vital interest in network servers such as Apache, the best way to get complete control is to install the servers yourself.

安装诸如您在本章前面构建的 coreutils 软件包中的软件包(如 ls、cat 等)等软件包并没有太大意义,除非您正在构建一个非常定制的系统。

另一方面,如果您对诸如 Apache 等网络服务器有重要兴趣,获得完全控制的最佳方法是自己安装服务器。

16.4.1 Where to Install (安装位置)

The default prefix in GNU autoconf and many other packages is /usr/local, the traditional directory for locally installed software. Operating system upgrades ignore /usr/local, so you won’t lose anything installed there during an operating system upgrade and for small local software installations, /usr/local is fine. The only problem is that if you have a lot of custom software installed, this can turn into a terrible mess. Thousands of odd little files can make their way into the /usr/local hierarchy, and you may have no idea where the files came from.

在GNU autoconf和许多其他软件包中,默认的前缀是/usr/local,这是本地安装软件的传统目录。

操作系统升级会忽略/usr/local,因此在操作系统升级期间,您不会丢失在那里安装的任何内容,对于小型本地软件安装来说,/usr/local是可以的。

唯一的问题是,如果您安装了大量自定义软件,这可能会变成一团糟。

成千上万个奇怪的小文件可能会进入/usr/local层次结构,而您可能不知道这些文件是从哪里来的。

If things really start to get unruly, you should create your own packages as described in 16.3.2 Installing Using a Packaging Tool.

如果事情真的开始变得混乱,您应该按照16.3.2中描述的方式创建自己的软件包进行安装。

16.5 Applying a Patch(应用补丁)

Most changes to software source code are available as branches of the developer’s online version of the source code (such as a git repository). However, every now and then, you might get a patch that you need to apply against source code to fix bugs or add features. You may also see the term diff used as a synonym for patch, because the diff program produces the patch.

大多数软件源代码的更改都以开发者在线版本的源代码分支的形式提供(比如一个 git 仓库)。

然而,偶尔你可能会收到一个需要应用到源代码中以修复错误或添加功能的补丁。

你可能也会看到术语 diff 被用作 patch 的同义词,因为 diff 程序产生了补丁。

The beginning of a patch looks something like this:

一个补丁的开头看起来像这样:

代码语言:sh
复制
--- src/file.c.orig 2015-07-17 14:29:12.000000000 +0100 
+++ src/file.c 2015-09-18 10:22:17.000000000 +0100 @@ -2,16 +2,12 
@@ Patches usually 

Patches usually contain alterations to more than one file. Search the patch for three dashes in a row (---) to see the files that have alterations and always look at the beginning of a patch to determine the required working directory. Notice that the preceding example refers to src/file.c. Therefore, you should change to the directory that contains src before applying the patch, not to the src directory itself.

补丁通常包含对多个文件的修改。

在补丁中搜索三个连续的短横线(---),以查看哪些文件有修改,并始终查看补丁的开头以确定所需的工作目录。请注意,上面的示例涉及到 src/file.c。

因此,在应用补丁之前,你应该切换到包含 src 的目录,而不是 src 目录本身。

To apply the patch, run the patch command:

要应用补丁,运行 patch 命令:

代码语言:sh
复制
$ patch -p0 < patch_file 

If everything goes well, patch exits without a fuss, leaving you with an updated set of files. However, patch may ask you this question:

如果一切顺利,补丁会无需多言地退出,留下一组更新后的文件。

然而,补丁可能会问你这个问题:

代码语言:sh
复制
File to patch: 

This usually means that you are not in the correct directory, but it could also indicate that your source code does not match the source code in the patch. In this case, you’re probably out of luck: Even if you could identify some of the files to patch, others would not be properly updated, leaving you with source code that you could not compile.

这通常意味着你不在正确的目录中,但也可能表明你的源代码与补丁中的源代码不匹配。

在这种情况下,你可能就没那么幸运了:即使你能识别出部分需要打补丁的文件,其他文件也不会被正确更新,导致你得到无法编译的源代码。

In some cases, you might come across a patch that refers to a package version like this:

在某些情况下,你可能会遇到一个参考了类似包版本的补丁,如下所示:

代码语言:sh
复制
--- package-3.42/src/file.c.orig 2015-07-17 14:29:12.000000000 +0100 
+++ package-3.42/src/file.c 2015-09-18 10:22:17.000000000 +0100 

If you have a slightly different version number (or you just renamed the directory), you can tell patch to strip leading path components. For example, say you were in the directory that contains src (as before). To tell patch to ignore the package-3.42/ part of the path (that is, strip one leading path component), use -p1:

如果你的版本号略有不同(或者你只是重命名了目录),你可以告诉 patch 忽略前导路径组件。

例如,假设你在包含 src 的目录中(如前所述)。

为了告诉 patch 忽略路径中的 package-3.42/ 部分(即去掉一个前导路径组件),可以使用 -p1:

代码语言:sh
复制
$ patch -p1 < patch_file 

16.6 Troubleshooting Compiles and Installations (编译和安装故障排除)

If you understand the difference between compiler errors, compiler warnings, linker errors, and shared library problems as described in Chapter 15, you shouldn’t have too much trouble fixing many of the glitches that arise when building software. This section covers some common problems. Although you’re unlikely to run into any of these when building using autoconf, it never hurts to know what these kinds of problems look like.

如果你理解编译器错误、编译器警告、链接器错误以及共享库问题的区别,就不会在构建软件时遇到太多麻烦。本节涵盖了一些常见问题。

虽然在使用autoconf构建时不太可能遇到这些问题,但了解这些问题的表现形式也无妨。

Before covering specifics, make sure that you can read certain kinds of make output. It’s important to know the difference between an error and an ignored error. The following is a real error that you need to investigate:

在介绍具体问题之前,请确保你能够阅读某些类型的make输出。

了解错误和被忽略的错误之间的区别很重要。以下是一个真实的错误,需要你进行调查:

代码语言:sh
复制
make: *** [target] Error 1 

However, some Makefiles suspect that an error condition might occur but know that these errors are harmless. You can usually disregard any messages like this:

然而,有些Makefile怀疑可能会出现错误条件,但知道这些错误是无害的。

通常你可以忽略类似以下的任何消息:

代码语言:sh
复制
make: *** [target] Error 1 (ignored) 

Furthermore, GNU make often calls itself many times in large packages, with each instance of make in the error message marked with N, where N is a number. You can often quickly find the error by looking at the make error that comes directly after the compiler error message. For example:

此外,GNU make 在大型软件包中经常多次调用自身,每个make实例在错误消息中用N标记,其中N是一个数字。

通常你可以通过查看直接在编译器错误消息之后出现的make错误来快速找到错误。例如:

代码语言:sh
复制
[compiler error message involving file.c]
make[3]: *** [file.o] Error 1
make[3]: Leaving directory '/home/src/package-5.0/src'
make[2]: *** [all] Error 2
make[2]: Leaving directory '/home/src/package-5.0/src'
make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory 
'/home/src/package-5.0/'
make: *** [all] Error 2

The first three lines practically give it away: The trouble centers around file.c located in /home/src/package5.0/src. Unfortunately, there is so much extra output that it can be difficult to spot the important details. Learning how to filter out the subsequent make errors goes a long way toward digging out the real cause.

前三行几乎透露了问题所在:问题集中在/home/src/package-5.0/src目录中的file.c文件。

不幸的是,有太多额外的输出,很难发现重要的细节。

学会如何过滤后续的make错误对于找出真正的原因至关重要。

16.6.1 Specific Errors (具体错误)

Here are some common build errors that you might encounter.

以下是一些您可能会遇到的常见构建错误

problem

Compiler error message: (编译器错误信息)

代码语言:sh
复制
src.c:22: conflicting types for 'item' 

/usr/include/file.h:47: previous declaration of 'item'

Explanation and Fix (解释和修复)

The programmer made an erroneous redeclaration of item on line 22 of src.c. You can usually fix this by removing the offending line (with a comment, an #ifdef, or whatever works).

程序员在 src.c 第 22 行对 item 进行了错误的重新声明。通常情况下,删除违规行(注释、#ifdef 或其他可行方法)即可解决这个问题。

Problem

Compiler error message:

编译器错误信息:

代码语言:sh
复制
src.c:37: 'time_t' undeclared (first use this function) 
--snip-- 
src.c:37: parse error before '...' 

Explanation and Fix (解释和修复)

The programmer forgot a critical header file. The manual pages are the best way to find the missing header file. First, look at the offending line (in this case, line 37 in src.c). It’s probably a variable declaration like the following:

程序员忘记了一个关键的头文件。手册页是找到缺失头文件的最佳方法。

首先,查看出错的行(在这种情况下,是 src.c 中的第37行)。它可能是一个类似以下的变量声明:

代码语言:sh
复制
time_t v1; 

Search forward for v1 in the program for its use around a function call. For example:

向前搜索程序中的 v1,找到它在函数调用周围的使用情况。

例如:

代码语言:sh
复制
v1 = time(NULL); 

Now run man 2 time or man 3 time to look for system and library calls named time(). In this case, the section 2 manual page has what you need:

现在运行 man 2 time 或 man 3 time 来查找名为 time() 的系统和库调用。

在这种情况下,第2节手册页中有你需要的信息:

代码语言:sh
复制
SYNOPSIS 
#include <time.h> 
time_t time(time_t *t); 

This means that time() requires time.h. Place #include at the beginning of src.c and try again.

这意味着 time() 需要 time.h。

在 src.c 的开头加上 #include,然后再次尝试。

Problem

Compiler (preprocessor) error message:

编译器(预处理器)错误信息:

代码语言:sh
复制
src.c:4: pkg.h: No such file or directory 
(long list of errors follows)

Explanation and Fix (解释和修复)

The compiler ran the C preprocessor on src.c but could not find the pkg.h include file. The source code likely depends on a library that you need to install, or you may just need to provide the compiler with the nonstandard include path. Usually, you will just need to add a -I include path option to the C preprocessor flags (CPPFLAGS). (Keep in mind that you might also need a -L linker flag to go along with the include files.)

编译器在 src.c 上运行了 C 预处理器,但找不到 pkg.h 包含文件。

源代码可能依赖于一个你需要安装的库,或者你可能只需要为编译器提供非标准的包含路径。

通常情况下,你只需要向 C 预处理器标志(CPPFLAGS)添加一个 -I 包含路径选项。

(请记住,你可能还需要一个 -L 链接器标志与包含文件一起使用。)

If it doesn’t look as though you’re missing a library, there’s an outside chance that you’re attempting a compile for an operating system that this source code does not support. Check the Makefile and README files for details about platforms. If you’re running a Debian-based distribution, try the apt-file command on the header filename: $ apt-file search pkg.h

如果看起来你没有缺少库,有可能是你正在尝试为不支持该源代码的操作系统进行编译。

查看 Makefile 和 README 文件以获取关于平台的详细信息。

如果你正在运行基于 Debian 的发行版,请尝试在头文件名上使用 apt-file 命令:$ apt-file search pkg.h

This might find the development package that you need. For distributions that provide yum, you can try this instead:

这可能会找到你需要的开发包。

对于提供 yum 的发行版,你可以尝试这个方法:

代码语言:sh
复制
$ yum provides */pkg.h 

Problem

代码语言:sh
复制
make error message: 
make: prog: 
	Command not found 

Explanation and Fix (解释和修复)

To build the package, you need prog on your system. If prog is something like cc, gcc, or ld, you don’t have the development utilities installed on your system. On the other hand, if you think prog is already installed on your system, try altering the Makefile to specify the full pathname of prog.

构建该软件包时,您需要在系统上安装 prog。

如果 prog 是类似 cc、gcc 或 ld 这样的东西,那么您的系统上可能没有安装开发工具。

另一方面,如果您认为 prog 已经安装在系统上,可以尝试修改 Makefile 文件以指定 prog 的完整路径。

In rare cases, make builds prog and then uses prog immediately, assuming that the current directory (.) is in your command path. If your $PATH does not include the current directory, you can edit the Makefile and change prog to ./prog. Alternatively, you could append . to your path temporarily.

在罕见情况下,make 命令会编译 prog 并立即使用 prog,假设当前目录 (.) 在您的命令路径中。

如果您的 $PATH 环境变量不包括当前目录,您可以编辑 Makefile 文件将 prog 更改为 ./prog。

另外,您也可以临时将 . 添加到您的路径中。

16.7 Looking Forward

We’ve only touched on the basics of building software. Here are some more topics that you can explore after you get the hang of your own builds: o Understanding how to use build systems other than autoconf, such as CMake and SCons. o

我们只是简单地涉及了构建软件的基础知识。

在掌握了自己构建的要领之后,以下是一些你可以探索的更多主题:

o 了解如何使用除了 autoconf 之外的构建系统,比如 CMake 和 SCons。o

Setting up builds for your own software. If you’re writing your own software, you want to choose a build system and learn to use it. For GNU autoconf packaging, Autotools by John Calcote (No Starch Press, 2010) can help you out. o

为你自己的软件设置构建。如果你正在编写自己的软件,你需要选择一个构建系统并学会如何使用它。

对于 GNU autoconf 打包,John Calcote 的《Autotools》(No Starch Press,2010)可以帮助你。o

Compiling the Linux kernel. The kernel’s build system is completely different from that of other tools. It has its own configuration system tailored to customizing your own kernel and modules. The procedure is straightforward, though, and if you understand how the boot loader works, you won’t have any trouble with it. However, you should be careful when doing so; make sure that you always keep your old kernel handy in case you can’t boot with a new one. o

编译 Linux 内核。内核的构建系统与其他工具完全不同。

它有自己的配置系统,专门用于定制自己的内核和模块。

尽管过程很简单,但如果你了解引导加载程序的工作原理,就不会遇到任何问题。

然而,在这样做时,你应该小心;确保始终保留旧内核,以防新内核无法启动。o

Distribution-specific source packages. Linux distributions maintain their own versions of software source code as special source packages. Sometimes you can find useful patches that expand functionality or fix problems in otherwise unmaintained packages. The source package management systems include tools for automatic builds, such as Debian’s debuild and the RPM-based mock.

特定于发行版的源代码包

Linux 发行版维护其自己的软件源代码版本作为特殊的源代码包。

有时你可以找到有用的补丁,扩展功能修复未维护软件包中的问题。

源代码包管理系统包括用于自动构建的工具,比如 Debian 的 debuild 和基于 RPM 的 mock。

Building software is often a stepping-stone to learning about programming and software development. The tools you’ve seen in the past two chapters take the mystery out of where your system software came from. It’s not difficult to take the next steps of looking inside the source code, making changes, and creating your own software.

构建软件通常是学习编程和软件开发的一个基石。

在过去两章中看到的工具揭示了系统软件的来源之谜。查看源代码、进行更改并创建自己的软件并不困难,这是迈向下一步的重要步骤。

欢迎关注笔者公众号“懒时小窝”获取更多内容。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第 16 章 Introduction to Compiling Software From C Source Code(从 C 源代码编译软件入门)
  • 16.2 Unpacking C Source Packages(解压 C 源代码包)
    • 16.2.1 Where to Start
    • 16.3 GNU Autoconf
      • 16.3.1 An Autoconf Example(Autoconf 示例)
        • 16.3.2 Installing Using a Packaging Tool(使用包管理工具进行安装)
          • 16.3.3 configure Script Options
            • 16.3.4 Environment Variables(环境变量)
              • 16.3.5 Autoconf Targets ( Autoconf 目标 )
                • 16.3.6 Autoconf Log Files (Autoconf 日志文件)
                  • 16.3.7 pkg-config
                    • How pkg-config Works (pkg-config的工作原理)
                    • Installing pkg-config Files in Nonstandard Locations
                  • 16.4.1 Where to Install (安装位置)
                  • 16.5 Applying a Patch(应用补丁)
                  • 16.6 Troubleshooting Compiles and Installations (编译和安装故障排除)
                    • 16.6.1 Specific Errors (具体错误)
                      • problem
                      • Explanation and Fix (解释和修复)
                      • Problem
                      • Explanation and Fix (解释和修复)
                      • Problem
                      • Explanation and Fix (解释和修复)
                      • Problem
                      • Explanation and Fix (解释和修复)
                  • 16.7 Looking Forward
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档