简单的程序,用一些标准的 Makefile 就可以解决所有问题。
一般过于复杂的系统,可以使用 autoconf 和 automake 来解决问题。
对于一些不大不小的项目,就需要稍微深入的了解 Makefile,这里有一个对于 Makefile 非常好的说明,(http://www.chinaunix.net/old_jh/23/408225.html),但是个人感觉写的有点多了,可以拿它当一个手册来看。
对于我遇到的项目,其实并不复杂,比最简单的 Makefile 就多做了以下的内容:
- 但是由于有多个模块共存,所以将他们放在了不同的目录当中;
- 根据不同的操作系统,需要变更一些编译选项;
后来发现可以使用 $(MAKE) 这个宏来进行;并且在测试当中,直接使用 SHELL的cd 命令是无法转换工作目录的。
ALL: ${OBJS}
@$(MAKE) moduleinside
${MY_CC} -o ${EXE} ${OBJS} ${LIB_DIRS} ${LIBS} ${STATIC_LIBS}
strip ${EXE}
%.o:%.c ${HEADERS}
${MY_CC} -g ${INCLUDE_DIRS} -c $< -o $@
moduleinside:
@list='$(ALL_DIRS)'; for subdir in $$list; do \
test "$$subdir" = . || (cd $$subdir && $(MAKE)); \
done
上面的代码很简单,All 标签是给Make的主标签,通过 $(MAKE) 宏来执行运行哪个Makefile文件。$(MAKE) 这个宏,可以理解为相当于打入 make 命令。
所有的模块目录,写入 $(ALL_DIRS) 变量中,然后通过 for 循环取出,然后执行其自己的Makefile文件。
运行完成以后,Make 会自动将工作目录返回当前目录,所以只需要cd出去,而不需要管回来的命令。
对于每个子目录中的 Makefile 就按照标准的 Makefile 完成就可以了。
如果有什么需要设定的变量,要在其他目录中的 Makefile 中继承。
export CC = gcc
可以像上面这样声明一下就可以了。
因为我们的需要在多种系统中进行编译,每个系统一些动态库的挂接可能是不一样的,这样我们需要进行操作系统的处理。
RUN_OS = $(shell uname) # 当前的系统
ALL: ${OBJS}
@$(MAKE) moduleinside
ifeq ($(firstword $(RUN_OS)), SunOS)
${MY_CC} -o ${EXE} ${OBJS} ${LIB_DIRS_SUN} ${LIBS_SUN} ${STATIC_LIBS}
else
${MY_CC} -o ${EXE} ${OBJS} ${LIB_DIRS} ${LIBS} ${STATIC_LIBS}
endif
strip ${EXE}
这里 RUN_OS 变量的获取方式,可以通过 uname 命令的结果拿到当前的执行系统。然后 通过 ifeq 来进行与目标系统的比较。
这里可能会注意到,使用了一个 $(firstword <target>) 的内置函数,为什么使用这个函数呢?
RUN_OS = $(shell uname) # 当前的系统
RUN_OS = $(shell uname)
可以看到,上面两行内容应该是完全一样的,但是在 Makefile 当中通过上面定义的变量,通过 ifeq 进行结果判断时竟然是不一样的。
原因就是变量命名以后,后面有多余的空格或者TAB或者其他 OOXX。
这样,就需要使用一个 $(firstword <target>) 函数进行处理,当然,你也可以写的时候注意一些,不要在变量命名后面添加任何多余的字符和注释等等内容。