前晚写了一段代码,用于测试Linux下典型的静态库和动态库对全局符号的不同处理.作为Linux C/CPP程序员,一般对静态库和动态库的使用都十分频繁,虽然也出现过静态库链接顺序导致符号无法解析的错误.但是大体理解静态库和动态库并不困难,但是真正理解静态库和动态库在链接和(或)加载过程中的原理,还是得深入阅读一些文档,写一些测试代码才能有直观的掌握.
如果您对测试过程不太感兴趣(确实这个过程比较乏味),请跳跃到最后一段,直接看结论即可.
给出测试代码(文末给出源码包,遵循GPL发布):
6个源代码文件(test_d.c,test.h,test_lib.c,test_lib.h,test_o.c,test_s.c).
1个Makefile(文末给出Makefile的内容,可以当作一个模板使用).
生成1个动态库文件(libtest_d.so),1个静态库文件(libtest_s.a),3个可执行目标文件(test_lib_d,test_lib_s,test_lib_o).
还有其他3个和源码文件(*.c)相对应的可重定位目标文件(*.o).
[yhzh@localhost 20090530]$ ls
Makefile test_d.c test.h test_lib.c test_lib.h test_o.c test_s.c
[yhzh@localhost 20090530]$ ls
libtest_d.so Makefile test_d.o test_lib.c test_lib.h test_lib.o test_o.c test_s.c
libtest_s.a test_d.c test.h test_lib_d test_lib_o test_lib_s test_o.o test_s.o
动态库的生成, 源码test_d.c, 定义了两个全局函数,自增一个静态变量和打印变量之值. 生成方法:
gcc -o libtest_d.so test_d.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -shared
[yhzh@localhost 20090530]$ cat test_d.c
#include "test.h"
#include "test_lib.h"
static int a = 0;
int test_inc()
{
fprintf(stdout, "[d]Begin in %s, static a is %d\n", __FILE__, a);
a++;
fprintf(stdout, "[d]End in %s, static a is %d\n", __FILE__, a);
return a;
}
void print_d()
{
fprintf(stdout, "[d]In %s[%s], static a is %d\n", __FILE__, __func__, a);
return;
}
void addition_d()
{
fprintf(stdout, "[d]In %s[%s], addition print\n", __FILE__, __func__);
return;
}
静态库的生成, 源码test_s.c, 和动态库的测试方法一致,定义了两个全局函数,自增一个静态变量和打印变量之值. 生成方法:
ar ru libtest_s.a test_s.o && ranlib libtest_s.a
源码test_lib.c, 调用同名的自增函数, 调用打印动态库函数, 调用或者不调用打印静态库函数. 生成两个可执行目标文件的方法:
gcc -o test_lib_d test_lib.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -ltest_d -ltest_s
gcc -o test_lib_s test_lib.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -ltest_s -ltest_d
以不同顺序加载静态库和动态库生成可执行目标文件.
A部分: 运行生成的test_lib_d 和 test_lib_s, 可以得到如下结果:
这个结果似乎很出乎意料, 为什么首先加载动态库时, 调用test_inc()也是进入了静态库中定义的函数中呢?
B部分: 当注释掉test_lib.c中的两行print_s():
然后再编译运行,可以得到如下结果:
这个结果才是意料之中(可以看运行结果,也可以看链接之后的可执行目标文件的大小).要理解这样的结果,必须理解链接器在链接静态库和动态库的过程中处理符号表的原理.如果能理解在运行可执行目标文件时,加载器的工作原理会更完美. 对于定义在多处的同样的全局符号名(例如分别定义在静态库中,动态库中和可重定位目标文件),在链接器链接过程中:
对于动态库中的全局符号是这样处理的:
1. 将动态库中一些符号信息和重定位信息拷贝进新目标文件的符号表(代码和数据都不处理).选择需要拷贝的符号是在新目标文件的符号表中未知的.
2. 将从动态库中引入的符号表中的head index域设置为UNDEF,value设置为0(可以通过readelf -s/nm/objdump -t查看),这样做的目的是,方便装载器运行时可以解析动态库文件中的代码和数据引用.
对于静态库中的全局符号是这样处理的:
1. 先扫描新目标文件的符号表中未定义域(U域),寻找定义在静态库中的符号信息.
2. 如果存在某个符号信息或者重定位信息相对应,可以让未定义引用的符号从U域转移出来的话,则拷贝整个静态库的全局符号信息和重定位信息过来,并做head index和value调整.
3. 如果不存在任何一个符号信息或者重定位信息相对应,则放弃拷贝静态库的任何内容.
因此,在链接器同时处理既有可重定位目标文件,静态库文件和动态库文件时:
1. 在静态库文件之间,链接顺序可以会导致未知符号错误(连接完毕之后,U域不为空),解决办法是调整静态库顺序,或交叉链接相互依赖的静态库.
2. 在静态库和动态库文件之间,这个关系比较复杂.如果链接动态库在前,而静态库在后, 那么只要静态库中没有U域中相应的任何一个全局符号,也就是链接器主动放弃操作静态库, 那么定义在动态库中的全局符号能得到保存;一旦存在任何一个全局的符号, 动态库中的全局同名符号, 会被静态库中的符号覆盖, 不管是否在静态库中强制设置了weak BIND属性,这个属性只对可重定位目标文件有效(这里不列出,源码包了也做了这个测试). 如果静态库在前,动态库在后,直接按流程链接即可.
总之,在Linux平台(2.6.25/29),在加载多处定义的全局符号是,静态库优先级似乎高于动态库.如果对此感兴趣,可以参考< CS:APP >的第七章和< LINKER and LOADER >全书.
附Makefile:
下载文件
如果您对测试过程不太感兴趣(确实这个过程比较乏味),请跳跃到最后一段,直接看结论即可.
给出测试代码(文末给出源码包,遵循GPL发布):
6个源代码文件(test_d.c,test.h,test_lib.c,test_lib.h,test_o.c,test_s.c).
1个Makefile(文末给出Makefile的内容,可以当作一个模板使用).
生成1个动态库文件(libtest_d.so),1个静态库文件(libtest_s.a),3个可执行目标文件(test_lib_d,test_lib_s,test_lib_o).
还有其他3个和源码文件(*.c)相对应的可重定位目标文件(*.o).
引用
[yhzh@localhost 20090530]$ ls
Makefile test_d.c test.h test_lib.c test_lib.h test_o.c test_s.c
[yhzh@localhost 20090530]$ ls
libtest_d.so Makefile test_d.o test_lib.c test_lib.h test_lib.o test_o.c test_s.c
libtest_s.a test_d.c test.h test_lib_d test_lib_o test_lib_s test_o.o test_s.o
动态库的生成, 源码test_d.c, 定义了两个全局函数,自增一个静态变量和打印变量之值. 生成方法:
gcc -o libtest_d.so test_d.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -shared
引用
[yhzh@localhost 20090530]$ cat test_d.c
#include "test.h"
#include "test_lib.h"
static int a = 0;
int test_inc()
{
fprintf(stdout, "[d]Begin in %s, static a is %d\n", __FILE__, a);
a++;
fprintf(stdout, "[d]End in %s, static a is %d\n", __FILE__, a);
return a;
}
void print_d()
{
fprintf(stdout, "[d]In %s[%s], static a is %d\n", __FILE__, __func__, a);
return;
}
void addition_d()
{
fprintf(stdout, "[d]In %s[%s], addition print\n", __FILE__, __func__);
return;
}
静态库的生成, 源码test_s.c, 和动态库的测试方法一致,定义了两个全局函数,自增一个静态变量和打印变量之值. 生成方法:
ar ru libtest_s.a test_s.o && ranlib libtest_s.a
引用
[yhzh@localhost 20090530]$ cat test_s.c
#include "test.h"
#include "test_lib.h"
static int a = 0;
/* add __attribute__((weak)) here? */
int test_inc()
{
fprintf(stdout, "[s]Begin in %s, static a is %d\n", __FILE__, a);
a++;
fprintf(stdout, "[s]End in %s, static a is %d\n", __FILE__, a);
return a;
}
void print_s()
{
fprintf(stdout, "[s]In %s[%s], static a is %d\n", __FILE__, __func__, a);
return;
}
void addition_s()
{
fprintf(stdout, "[s]In %s[%s], addition print\n", __FILE__, __func__);
return;
}
#include "test.h"
#include "test_lib.h"
static int a = 0;
/* add __attribute__((weak)) here? */
int test_inc()
{
fprintf(stdout, "[s]Begin in %s, static a is %d\n", __FILE__, a);
a++;
fprintf(stdout, "[s]End in %s, static a is %d\n", __FILE__, a);
return a;
}
void print_s()
{
fprintf(stdout, "[s]In %s[%s], static a is %d\n", __FILE__, __func__, a);
return;
}
void addition_s()
{
fprintf(stdout, "[s]In %s[%s], addition print\n", __FILE__, __func__);
return;
}
源码test_lib.c, 调用同名的自增函数, 调用打印动态库函数, 调用或者不调用打印静态库函数. 生成两个可执行目标文件的方法:
gcc -o test_lib_d test_lib.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -ltest_d -ltest_s
gcc -o test_lib_s test_lib.o -Wl,-rpath,/home/yhzh/code/c/test/20090530 -L/usr/local/lib -L/home/yhzh/tools/lib -L. -ltest_s -ltest_d
以不同顺序加载静态库和动态库生成可执行目标文件.
引用
[yhzh@localhost 20090530]$ cat test_lib.c
#include "test.h"
#include "test_lib.h"
int main(int argc, char **argv)
{
int a = 0;
a = test_inc();
print_d();
print_s();
fprintf(stdout, "[1]after test_inc, a is %d\n", a);
a = test_inc();
print_d();
print_s();
fprintf(stdout, "[2]after test_inc, a is %d\n", a);
return 0;
}
#include "test.h"
#include "test_lib.h"
int main(int argc, char **argv)
{
int a = 0;
a = test_inc();
print_d();
print_s();
fprintf(stdout, "[1]after test_inc, a is %d\n", a);
a = test_inc();
print_d();
print_s();
fprintf(stdout, "[2]after test_inc, a is %d\n", a);
return 0;
}
A部分: 运行生成的test_lib_d 和 test_lib_s, 可以得到如下结果:
引用
[yhzh@localhost 20090530]$ ./test_lib_d
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 1
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 2
[2]after test_inc, a is 2
[yhzh@localhost 20090530]$ ./test_lib_s
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 1
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 2
[2]after test_inc, a is 2
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 1
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 2
[2]after test_inc, a is 2
[yhzh@localhost 20090530]$ ./test_lib_s
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 1
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[s]In test_s.c[print_s], static a is 2
[2]after test_inc, a is 2
这个结果似乎很出乎意料, 为什么首先加载动态库时, 调用test_inc()也是进入了静态库中定义的函数中呢?
B部分: 当注释掉test_lib.c中的两行print_s():
引用
11 //print_s();
...
16 //print_s();
...
16 //print_s();
然后再编译运行,可以得到如下结果:
引用
[yhzh@localhost 20090530]$ ./test_lib_d
[d]Begin in test_d.c, static a is 0
[d]End in test_d.c, static a is 1
[d]In test_d.c[print_d], static a is 1
[1]after test_inc, a is 1
[d]Begin in test_d.c, static a is 1
[d]End in test_d.c, static a is 2
[d]In test_d.c[print_d], static a is 2
[2]after test_inc, a is 2
[yhzh@localhost 20090530]$ ./test_lib_s
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[2]after test_inc, a is 2
[d]Begin in test_d.c, static a is 0
[d]End in test_d.c, static a is 1
[d]In test_d.c[print_d], static a is 1
[1]after test_inc, a is 1
[d]Begin in test_d.c, static a is 1
[d]End in test_d.c, static a is 2
[d]In test_d.c[print_d], static a is 2
[2]after test_inc, a is 2
[yhzh@localhost 20090530]$ ./test_lib_s
[s]Begin in test_s.c, static a is 0
[s]End in test_s.c, static a is 1
[d]In test_d.c[print_d], static a is 0
[1]after test_inc, a is 1
[s]Begin in test_s.c, static a is 1
[s]End in test_s.c, static a is 2
[d]In test_d.c[print_d], static a is 0
[2]after test_inc, a is 2
这个结果才是意料之中(可以看运行结果,也可以看链接之后的可执行目标文件的大小).要理解这样的结果,必须理解链接器在链接静态库和动态库的过程中处理符号表的原理.如果能理解在运行可执行目标文件时,加载器的工作原理会更完美. 对于定义在多处的同样的全局符号名(例如分别定义在静态库中,动态库中和可重定位目标文件),在链接器链接过程中:
对于动态库中的全局符号是这样处理的:
1. 将动态库中一些符号信息和重定位信息拷贝进新目标文件的符号表(代码和数据都不处理).选择需要拷贝的符号是在新目标文件的符号表中未知的.
2. 将从动态库中引入的符号表中的head index域设置为UNDEF,value设置为0(可以通过readelf -s/nm/objdump -t查看),这样做的目的是,方便装载器运行时可以解析动态库文件中的代码和数据引用.
对于静态库中的全局符号是这样处理的:
1. 先扫描新目标文件的符号表中未定义域(U域),寻找定义在静态库中的符号信息.
2. 如果存在某个符号信息或者重定位信息相对应,可以让未定义引用的符号从U域转移出来的话,则拷贝整个静态库的全局符号信息和重定位信息过来,并做head index和value调整.
3. 如果不存在任何一个符号信息或者重定位信息相对应,则放弃拷贝静态库的任何内容.
因此,在链接器同时处理既有可重定位目标文件,静态库文件和动态库文件时:
1. 在静态库文件之间,链接顺序可以会导致未知符号错误(连接完毕之后,U域不为空),解决办法是调整静态库顺序,或交叉链接相互依赖的静态库.
2. 在静态库和动态库文件之间,这个关系比较复杂.如果链接动态库在前,而静态库在后, 那么只要静态库中没有U域中相应的任何一个全局符号,也就是链接器主动放弃操作静态库, 那么定义在动态库中的全局符号能得到保存;一旦存在任何一个全局的符号, 动态库中的全局同名符号, 会被静态库中的符号覆盖, 不管是否在静态库中强制设置了weak BIND属性,这个属性只对可重定位目标文件有效(这里不列出,源码包了也做了这个测试). 如果静态库在前,动态库在后,直接按流程链接即可.
总之,在Linux平台(2.6.25/29),在加载多处定义的全局符号是,静态库优先级似乎高于动态库.如果对此感兴趣,可以参考< CS:APP >的第七章和< LINKER and LOADER >全书.
附Makefile:
引用
##
## Copyright (C) 2008 Spark Zheng
##
## TAKE AS a common prototype of Makefile
##
PREFIX ?= /home/yhzh/tools
PROJECT_HOME ?= /home/yhzh/code/c/test/20090530
export PREFIX
export PROJECT_HOME
CC = gcc
CXX = g++
CFLAGS = -g -W -Wall -fPIC
CXXFLAGS = -g -W -Wall -fPIC
CFLAGS_WARN = -Wshadow -Wcast-qual -Winline -Wunreachable-code \
-Wredundant-decls -Wmissing-prototypes \
-Wstrict-prototypes -Wnested-externs
CXXFLAGS_WARN =
CFLAGS_PREVD = -D_DEBUG
CXXFLAGS_PREVD = -D_DEBUG
CFLAGS_OPT = -O2 -fomit-frame-pointer
CXXFLAGS_OPT = -O2
INCFLAGS := -I. -I./include -I.. -I../include -I$(PREFIX)/include
LDFLAGS := -Wl,-rpath,$(PROJECT_HOME) \
-L/usr/local/lib -L$(PREFIX)/lib -L.
LIB_LDFLAGS := $(LDFLAGS) -shared
LIBS = -lpthread -levent
RANLIB = ranlib
AR = ar
ARFLAGS = ru
INSTALL ?= install -c
UNINSTALL ?= rm -f
DIRS =
DIRS +=
TARGETS = $(EXEC_TARGETS) $(LIB_TARGETS)
EXEC_TARGETS = test_lib_d test_lib_s test_lib_o
LIB_TARGETS = libtest_d.so libtest_s.a
LIB_HEADS =
OBJECTS = $(EXEC_OBJECTS) $(LIB_OBJECTS)
EXEC_OBJECTS = test_lib.o
LIB_OBJECTS = test_d.o test_s.o test_o.o
all: $(TARGETS)
test_lib_d: $(EXEC_OBJECTS) $(LIB_TARGETS)
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_d -ltest_s
test_lib_s: $(EXEC_OBJECTS) $(LIB_TARGETS)
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_s -ltest_d
test_lib_o: $(EXEC_OBJECTS) $(LIB_TARGETS) test_o.o
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_s -ltest_d test_o.o
libtest_d.so: test_d.o
$(CC) -o $@ $< $(LIB_LDFLAGS)
libtest_s.a: test_s.o
$(AR) $(ARFLAGS) $@ $<
$(RANLIB) $@
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
.c.o :
$(CC) -o $@ -c $< $(CFLAGS) $(CFLAGS_WARN) $(CFLAGS_PREVD) $(INCFLAGS)
count:
-wc *.c *.cc *.C *.cpp *.h *.hpp
set -e; for i in $(DIRS); do \
echo "make count enter $$i."; \
make -C $$i count; \
echo "make count leave $$i."; \
done;
clean:
-rm -f $(OBJECTS) $(TARGETS) *.swp *~ core.*
set -e; for i in $(DIRS); do \
echo "make clean enter $$i."; \
make -C $$i clean; \
echo "make clean leave $$i."; \
done;
install:
ifneq ("$(EXEC_TARGETS)","")
$(INSTALL) $(EXEC_TARGETS) $(PREFIX)/bin/
endif
ifneq ("$(LIB_TARGETS)","")
$(INSTALL) $(LIB_TARGETS) $(PREFIX)/lib/
endif
ifneq ("$(LIB_HEADS)","")
$(INSTALL) $(LIB_HEADS) $(PREFIX)/include/
endif
set -e; for i in $(DIRS); do \
echo "make install enter $$i."; \
make -C $$i install; \
echo "make intall leave $$i."; \
done;
uninstall:
ifneq ("$(EXEC_TARGETS)","")
set -e; for i in $(EXEC_TARGETS); do \
$(UNINSTALL) $(PREFIX)/bin/$$i; \
done;
endif
ifneq ("$(LIB_TARGETS)","")
set -e; for i in $(LIB_TARGETS) do \
$(UNINSTALL) $(PREFIX)/lib/$$i; \
done;
endif
ifneq ("$(LIB_HEADS)","")
set -e; for i in $(LIB_HEADS) do \
$(UNINSTALL) $(PREFIX)/include/$$i; \
done;
endif
set -e; for i in $(DIRS); do \
echo "make uninstall enter $$i."; \
make -C $$i uninstall; \
echo "make uninstall leave $$i."; \
done;
.PHONY: all
.PHONY: count
.PHONY: clean
.PHONY: install
.PHONY: uninstall
## Copyright (C) 2008 Spark Zheng
##
## TAKE AS a common prototype of Makefile
##
PREFIX ?= /home/yhzh/tools
PROJECT_HOME ?= /home/yhzh/code/c/test/20090530
export PREFIX
export PROJECT_HOME
CC = gcc
CXX = g++
CFLAGS = -g -W -Wall -fPIC
CXXFLAGS = -g -W -Wall -fPIC
CFLAGS_WARN = -Wshadow -Wcast-qual -Winline -Wunreachable-code \
-Wredundant-decls -Wmissing-prototypes \
-Wstrict-prototypes -Wnested-externs
CXXFLAGS_WARN =
CFLAGS_PREVD = -D_DEBUG
CXXFLAGS_PREVD = -D_DEBUG
CFLAGS_OPT = -O2 -fomit-frame-pointer
CXXFLAGS_OPT = -O2
INCFLAGS := -I. -I./include -I.. -I../include -I$(PREFIX)/include
LDFLAGS := -Wl,-rpath,$(PROJECT_HOME) \
-L/usr/local/lib -L$(PREFIX)/lib -L.
LIB_LDFLAGS := $(LDFLAGS) -shared
LIBS = -lpthread -levent
RANLIB = ranlib
AR = ar
ARFLAGS = ru
INSTALL ?= install -c
UNINSTALL ?= rm -f
DIRS =
DIRS +=
TARGETS = $(EXEC_TARGETS) $(LIB_TARGETS)
EXEC_TARGETS = test_lib_d test_lib_s test_lib_o
LIB_TARGETS = libtest_d.so libtest_s.a
LIB_HEADS =
OBJECTS = $(EXEC_OBJECTS) $(LIB_OBJECTS)
EXEC_OBJECTS = test_lib.o
LIB_OBJECTS = test_d.o test_s.o test_o.o
all: $(TARGETS)
test_lib_d: $(EXEC_OBJECTS) $(LIB_TARGETS)
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_d -ltest_s
test_lib_s: $(EXEC_OBJECTS) $(LIB_TARGETS)
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_s -ltest_d
test_lib_o: $(EXEC_OBJECTS) $(LIB_TARGETS) test_o.o
set -e; for i in $(DIRS); do \
echo "make all enter $$i."; \
make -C $$i; \
echo "make all leave $$i."; \
done;
$(CC) -o $@ $(EXEC_OBJECTS) $(LDFLAGS) -ltest_s -ltest_d test_o.o
libtest_d.so: test_d.o
$(CC) -o $@ $< $(LIB_LDFLAGS)
libtest_s.a: test_s.o
$(AR) $(ARFLAGS) $@ $<
$(RANLIB) $@
.SUFFIXES:
.SUFFIXES: .c .cc .C .cpp .o
.c.o :
$(CC) -o $@ -c $< $(CFLAGS) $(CFLAGS_WARN) $(CFLAGS_PREVD) $(INCFLAGS)
count:
-wc *.c *.cc *.C *.cpp *.h *.hpp
set -e; for i in $(DIRS); do \
echo "make count enter $$i."; \
make -C $$i count; \
echo "make count leave $$i."; \
done;
clean:
-rm -f $(OBJECTS) $(TARGETS) *.swp *~ core.*
set -e; for i in $(DIRS); do \
echo "make clean enter $$i."; \
make -C $$i clean; \
echo "make clean leave $$i."; \
done;
install:
ifneq ("$(EXEC_TARGETS)","")
$(INSTALL) $(EXEC_TARGETS) $(PREFIX)/bin/
endif
ifneq ("$(LIB_TARGETS)","")
$(INSTALL) $(LIB_TARGETS) $(PREFIX)/lib/
endif
ifneq ("$(LIB_HEADS)","")
$(INSTALL) $(LIB_HEADS) $(PREFIX)/include/
endif
set -e; for i in $(DIRS); do \
echo "make install enter $$i."; \
make -C $$i install; \
echo "make intall leave $$i."; \
done;
uninstall:
ifneq ("$(EXEC_TARGETS)","")
set -e; for i in $(EXEC_TARGETS); do \
$(UNINSTALL) $(PREFIX)/bin/$$i; \
done;
endif
ifneq ("$(LIB_TARGETS)","")
set -e; for i in $(LIB_TARGETS) do \
$(UNINSTALL) $(PREFIX)/lib/$$i; \
done;
endif
ifneq ("$(LIB_HEADS)","")
set -e; for i in $(LIB_HEADS) do \
$(UNINSTALL) $(PREFIX)/include/$$i; \
done;
endif
set -e; for i in $(DIRS); do \
echo "make uninstall enter $$i."; \
make -C $$i uninstall; \
echo "make uninstall leave $$i."; \
done;
.PHONY: all
.PHONY: count
.PHONY: clean
.PHONY: install
.PHONY: uninstall
下载文件
vps观察者
2009/07/30 03:39
在linux下编译我一直很头疼
分页: 1/1
1
1
模拟退火和遗传算法(4m


2009/06/01 13:37 | by 

