安装GCC 4.9

安装GCC
Install GCC

这是我找到的最简单的安装gcc,并且不需要root权限的方法 (https://gcc.gnu.org/wiki/InstallingGCC

这个方法适用与GCC的多个版本(4.6,4.7,4.8,4.9)

tar xzf gcc-4.6.2.tar.gz
cd gcc-4.6.2
./contrib/download_prerequisites
cd ..
mkdir objdir
cd objdir
$PWD/../gcc-4.6.2/configure --prefix=$HOME/gcc-4.6.2
make
make install

第七行:如果只需要64位编译器,不需要32位的,可以加上–disable-multilib
第八行:可以用-j N参数指定N个进程同时编译

gcc参数顺序

gcc参数顺序
Ordering of gcc parameters matters

2013年最后一个月最后一个星期谈一个奇怪的bug,我在尝试pinfo的时候,configure脚本会提示ncurses有问题:

checking location of curses.h file... /usr/include/ncurses.h
checking if curses is usable... no
configure: error: Curses not found. You need curses to compile pinfo

打开config.log文件,发现这个提示:

configure:12563: checking if curses is usable
configure:12587: gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c  >&5
/tmp/ccBLkSqd.o: In function `main':
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:38: undefined reference to `initscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:39: undefined reference to `printw'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:40: undefined reference to `stdscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:40: undefined reference to `wrefresh'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:41: undefined reference to `stdscr'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:41: undefined reference to `wgetch'
/net/fantasia/home/zhanxw/software/pinfo-0.6.10/conftest.c:42: undefined reference to `endwin'
collect2: ld returned 1 exit status

从这个命令行来看,curses库的头文件目录和库文件目录都对,但编译的时候(链接/tmp/ccBLkSqd.o)却找不到几个函数的定义(比如:initscr,printw等等)。为了重现这个看起来不该出现的问题,我把conftest.c创建好,然后用相同的gcc命令行编译代码,的确是可以重现错误。

更令我不解的是如果把conftest.c放到命令行的末尾,这个编译就能通过了:

# This does not work
gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c 
# This works
gcc -o conftest conftest.c -g -O2 -I/usr/include    -L/usr/lib -lncurses  

为什么把conftest.c移到“-L/usr/lib -lncurses”前面就能解决问题?
看到StackOverFlow上的一个帖子

这个帖子讲到gcc编译和链接一个源程序时,会保留参数的顺序,也就是说在链接时,把conftest.c放到前面和后面,效果不同。具体来讲:

conftest.c 放到后面

gcc -o conftest -g -O2 -I/usr/include    -L/usr/lib -lncurses conftest.c 
<=> 等价于
gcc -o conftest -g -O2 -L/usr/lib -lncurses tmporary_object_file.o

conftest.c 放到前面

gcc -o conftest -g -O2 -I/usr/include conftest.c -L/usr/lib -lncurses 
<=> 等价于
gcc -o conftest -g -O2 tmporary_object_file.o -L/usr/lib -lncurses 

也就是说,gcc在链接目标文件时,会依照命令行的顺序,对于每个在前面目标文件中没有定义的函数,都试着在其后面的目标文件找找,如果最后一个目标文件也找不到,就报错。因此在我们的例子里,tmporary_object_file.o一定要放到-L/usr/lib -lncurses前面才能正确编译。

回到本文最开始,我们如何让configure把conftest.c放到“-L”前面?答案不是改动configure,而是使用LIBS环境变量:

LIBS="-L/usr/lib -lncurses" ./configure

与LIBS不同,LDFLAGS会放到conftest.c前面,这样configure还是没法正确的使用configure。

在本文最后,回想我们本来的问题,就是说gcc的参数有一定的顺序要求。也许gcc的手册里写了顺序的要求,但是很少有人能够看完gcc的手册。如果见到类似问题,应该怎么办呢?我觉得可以有两个思路,第一个就是想想写gcc软件的人是怎么想的。比如现在gcc的选择就是一个很方便的方法,如果当前没某个函数没有定义,就在他后面制定的目标文件里找找。如果这个要求反过来,或者任何顺序都支持,那么链接的函数就会复杂(因为链接程序需要存储所有定义过的函数,参见链接);第二个就是想想传统的(或者标准的)写法是什么,把已知的经验和这个错误联系起来。比如configure的错误看起来很奇怪,但是先按照传统的写法先编译后链接,找到一个能凑合工作的方案(把conftest.c放到前面),再逐步改变到出错时的命令行,这个逐步的过程可以帮助查错也能巩固一下已有的知识。

GCC 相关设置(跨平台、不同版本)

查看gcc build-in specs

gcc -dumpspecs
-dumpspecs
Print the compiler's built-in specs---and don't do anything else. (This is used when GCC itself is being built.)

看gcc到哪些目录查找头文件:

gcc -print-search-dirs
-print-search-dirs
Print the name of the configured installation directory and a list of program and library directories gcc will search---and don't do anything else.

This is useful when gcc prints the error message installation problem, cannot exec cpp0: No such file or directory. To resolve this you either need to put cpp0 and the other compiler components where gcc expects to find them, or you can set the environment variable
GCC_EXEC_PREFIX to the directory where you installed them. Don't forget the trailing /.

看gcc编译到那个平台

gcc -Q -v
-Q Makes the compiler print out each function name as it is compiled, and print some statistics about each pass when it finishes.
zhanxw@host10-41.sph.umich.edu: ~/temp> gcc -Q -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/local/libexec/gcc/x86_64-apple-darwin10/4.7.1/lto-wrapper
Target: x86_64-apple-darwin10
Configured with: ../gcc-4.7.1/configure --prefix=/opt/local --build=x86_64-apple-darwin10 --enable-languages=c,c++,objc,obj-c++,lto,fortran,java --libdir=/opt/local/lib/gcc47 --includedir=/opt/local/include/gcc47 --infodir=/opt/local/share/info --mandir=/opt/local/share/man --datarootdir=/opt/local/share/gcc-4.7 --with-libiconv-prefix=/opt/local --with-local-prefix=/opt/local --with-system-zlib --disable-nls --program-suffix=-mp-4.7 --with-gxx-include-dir=/opt/local/include/gcc47/c++/ --with-gmp=/opt/local --with-mpfr=/opt/local --with-mpc=/opt/local --with-ppl=/opt/local --with-cloog=/opt/local --enable-cloog-backend=isl --enable-stage1-checking --disable-multilib --enable-lto --with-as=/opt/local/bin/as --with-ld=/opt/local/bin/ld --with-ar=/opt/local/bin/ar --with-bugurl=https://trac.macports.org/newticket --disable-ppl-version-check --with-pkgversion='MacPorts gcc47 4.7.1_2'
Thread model: posix
gcc version 4.7.1 (MacPorts gcc47 4.7.1_2)

看gcc默认开启了哪些FLAG(定义了哪些macro)

$cpp -dM <(echo "") #define __DBL_MIN_EXP__ (-1021) #define __FLT_MIN__ 1.17549435e-38F #define __DEC64_DEN__ 0.000000000000001E-383DD #define __CHAR_BIT__ 8 #define __WCHAR_MAX__ 2147483647 #define __DBL_DENORM_MIN__ 4.9406564584124654e-324 #define __FLT_EVAL_METHOD__ 0 #define __DBL_MIN_10_EXP__ (-307) #define __FINITE_MATH_ONLY__ 0 #define __DEC64_MAX_EXP__ 384 ... ...

和MacPorts 相关的——如何选择gcc

sudo port select --list gcc
sudo port select --set gcc gcc42 # default gcc in Mac Snow Leopard
sudo port select --set gcc mp-gcc47 # MacPorts GNU GCC 4.7

和平台相关的Macro定义:

其他参考
【1】StackOverflow上的讨论:哪个macro可以区分Mac和Linux Link
【2】SourceForge上Pre-defined C/C++ Compiler Macros,包括以下方面:
Standards
Compilers
Libraries
Operating Systems
Architectures
Devices
Link

Makefile技巧三则

Three Tricks to use Makefile Efficiently

最近读Protocol Buffer for C,发现一些Makefile的技巧

1. -MMD flag
Makefile比较麻烦的是写dependency。常用技巧是通过g++ -MM和sed脚本从.cpp源文件生成.d文件,这种方式可以解决自动生成依赖关系的问题,但其实有更好的方法:
g++ -MMD -c XXX.c
之后在Makefile里添加:
-include XXX.d

g++的文档提到-MMD:

-MMD
    Like -MD except mention only user header files, not system header files. 

除了-MMD外,有意思的g++参数,可以参见gcc flags 。另外这篇详细讲解Makefile dependency的文章Makefile Autodependency

2. Order-only prerequisites
order-only-prerequisites
用Makefile里的例子来说:

     OBJDIR := objdir
     OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
     
     $(OBJDIR)/%.o : %.c
             $(COMPILE.c) $(OUTPUT_OPTION) $<
     
     all: $(OBJS)
     
     $(OBJS): | $(OBJDIR)
     
     $(OBJDIR):
             mkdir $(OBJDIR)

$(OBJS)需要$(OBJDIR),所以需要把$(OBJS)放在$(OBJDIR)前面;但在建立$(OBJDIR)之后,$(OBJ)就不在需要根据$(OBJDIR)的时间调整了。

3. Multiple line variables (Eval function)
Makefile里的变量可以有多行,这种变量要用define来定义。
使用这种变量有两种场合:(1)Canned Recipes; (2)Eval-Function

第一种就是把常用的一组命令包装在一起,比如:

     define run-yacc =
     yacc $(firstword $^)
     mv y.tab.c $@
     endef

之后调用:

     foo.c : foo.y
             $(run-yacc)

第二种更加常用,比如下面的例子:

     PROGRAMS    = server client
     
     server_OBJS = server.o server_priv.o server_access.o
     server_LIBS = priv protocol
     
     client_OBJS = client.o client_api.o client_mem.o
     client_LIBS = protocol
     
     # Everything after this is generic
     
     .PHONY: all
     all: $(PROGRAMS)
     
     define PROGRAM_template =
      $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
      ALL_OBJS   += $$($(1)_OBJS)
     endef
     
     $(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
     
     $(PROGRAMS):
             $(LINK.o) $^ $(LDLIBS) -o $@
     
     clean:
             rm -f $(ALL_OBJS) $(PROGRAMS)

首先上面注释行以下都是通用(generic)的。
其次eval会两次展开(expand)变量名,所以$(1)会被扩展成prog(比如server),$$($(1)_OBJS)会被扩展成$(server_OBJS),之后会再次被扩展成server.o server_priv.o server_access.o

最精彩的地方是如何把上面3个技巧联合起来。下面看一下cloudwu的Protocol Buffer for C项目的Makefile片段:

BUILD = build

LIBSRCS = context.c varint.c array.c pattern.c register.c proto.c map.c alloc.c rmessage.c wmessage.c bootstrap.c stringpool.c
LIBNAME = libpbc.$(SO)

TESTSRCS = addressbook.c pattern.c
PROTOSRCS = addressbook.proto descriptor.proto

BUILD_O = $(BUILD)/o

all : lib test

lib : $(LIBNAME)

clean :
	rm -rf $(BUILD)

$(BUILD) : $(BUILD_O)

$(BUILD_O) :
	mkdir -p $@

LIB_O :=

define BUILD_temp
  TAR :=  $(BUILD_O)/$(notdir $(basename $(1)))
  LIB_O := $(LIB_O) $$(TAR).o
  $$(TAR).o : | $(BUILD_O)
  -include $$(TAR).d
  $$(TAR).o : src/$(1)
	$(CC) $(CFLAGS) -c -Isrc -I. -fPIC -o $$@ -MMD $$<
endef

$(foreach s,$(LIBSRCS),$(eval $(call BUILD_temp,$(s))))

$(LIBNAME) : $(LIB_O)
	cd $(BUILD) && $(CC) --shared -o $(LIBNAME) $(addprefix ../,$^)

有意思的地方是BUILD_temp,将每一个$(1)的值赋给TAR,之后$(TAR)在$(BUILD_O)目录下编译,编译时的依赖关系被-include到Makefile内部(include前的减号,-,表示要include的文件即使不存在也不报错),同时把编译好的$(TAR).o加入到LIB_O变量中(+=赋值)。