第 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源代碼安裝軟件包通常包括以下步驟:
- Unpack the source code archive.
- Configure the package.
- Run make to build the programs.
- Run make install or a distribution-specific install command to install the package.
- 解壓源代碼存檔。
- 配置軟件包。
- 運行make來構建程序。
- 運行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:
像下面這樣的輸出意味着這個軟件包可能可以安全解壓:
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 一樣):
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:
解壓這樣一個歸檔文件可能會在當前目錄留下一團糟。
為了避免這種情況,應該先創建一個新目錄並在解壓歸檔文件內容之前進入該目錄。
最後,要注意那些包含絕對路徑文件的軟件包,比如:
/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 命令:
$ ./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/獲取軟件包(通常最新版本是最好的),解壓縮,進入其目錄,並像這樣配置它:
$ ./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 命令:
$ 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 實際執行的操作:
$ 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 目錄之外),那麼可以進行實際安裝:
$ 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,具體操作如下:
# 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目錄下,依此類推。
通常情況下,你會想要修改這個前綴,像這樣:
$ ./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 宏,可以使用以下命令:
$ CPPFLAGS=-DDEBUG ./configure
NOTE You can also pass a variable as an option to configure; for example:
注意:您也可以將變量作為選項傳遞以進行配置;例如:
$ ./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中搜索,運行以下命令:
$ 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中查找,使用以下命令:
$ 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鏈接器選項:
$ 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中的減號,如下所示:
$ CPPFLAGS=Iinclude_dir ./configure
This yields an error like this:
這將導致如下錯誤:
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中查看,會發現如下內容:
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 程序,不僅用於廣告它們的頭文件和庫的位置,還用於指定編譯和鏈接程序所需的確切標誌。
語法如下:
$ pkg-config options package1 package2 ...
For example, to find the libraries required for OpenSSL, you can run this command:
例如,要查找 OpenSSL 所需的庫,你可以運行這個命令:
$ pkg-config --libs openssl
The output should be something like this:
輸出應該類似於:
-lssl -lcrypto
To see all libraries that pkg-config knows about, run this command:
要查看 pkg-config 知道的所有庫,運行這個命令:
$ 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目錄下):
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:
一個補丁的開頭看起來像這樣:
--- 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 命令:
$ 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:
如果一切順利,補丁會無需多言地退出,留下一組更新後的文件。
然而,補丁可能會問你這個問題:
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:
在某些情況下,你可能會遇到一個參考了類似包版本的補丁,如下所示:
--- 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:
$ 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輸出。
瞭解錯誤和被忽略的錯誤之間的區別很重要。以下是一個真實的錯誤,需要你進行調查:
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懷疑可能會出現錯誤條件,但知道這些錯誤是無害的。
通常你可以忽略類似以下的任何消息:
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錯誤來快速找到錯誤。例如:
[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: (編譯器錯誤信息)
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:
編譯器錯誤信息:
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行)。它可能是一個類似以下的變量聲明:
time_t v1;
Search forward for v1 in the program for its use around a function call. For example:
向前搜索程序中的 v1,找到它在函數調用周圍的使用情況。
例如:
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節手冊頁中有你需要的信息:
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:
編譯器(預處理器)錯誤信息:
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 的發行版,你可以嘗試這個方法:
$ yum provides */pkg.h
Problem
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.
構建軟件通常是學習編程和軟件開發的一個基石。
在過去兩章中看到的工具揭示了系統軟件的來源之謎。查看源代碼、進行更改並創建自己的軟件並不困難,這是邁向下一步的重要步驟。