更新旧版C makefile以包含C++源文件

tel*_*tel 2 c c++ legacy makefile gnu-make

我在计算生物物理实验室工作.我不是程序员,虽然我得到报酬就像一个人.这是我的问题:该实验室的主要产品是一个巨大的(50多个源文件)C程序.我需要让我们的实验室程序与另一个实验室的工具包一起工作,这恰好是一系列C++库(.a文件)的形式.我可以使用以下makefile为我们的程序编译主库:

CC      = gcc
#CC = icc
CFLAGS  = -g -Wall
#CFLAGS = -xT -openmp -I/opt/local/include -I/usr/local/include -I/opt/GDBM/include
#CFLAGS  = -O3 -g -Wall -I/opt/GDBM/include -fopenmp

LIB     = mcce.a
AR      = ar
ARFLAGS = rvs


SRC     =  all.c       ddvv.c          geom_3v_onto_3v.c  ins_res.c         strip.c\
app.c       del_conf.c      geom_apply.c       line_2v.c         vdotv.c\
avv.c       del_prot.c      geom_inverse.c     load_all_param.c  vector_normalize.c\
avvvv.c     del_res.c       geom_move.c        load_param.c      vector_vminusv.c\
cpy_conf.c  det3.c          geom_reset.c       mxm4.c            vector_vplusv.c\
cpy_prot.c  det4.c          geom_roll.c        new_prot.c        vector_vxv.c\
cpy_res.c   dll.c           get_files.c        param_get.c  param_exist.c\
db_close.c  dvv.c           iatom.c            param_sav.c\
db_open.c   free_strings.c  ins_conf.c         plane_3v.c pdbline2atom.c\
premcce.c   init.c          load_pdb.c write_pdb.c   rotamers.c assign_rad.c get_connect12.c\
surfw.c   vdw.c vdw_conf.c  shuffle_n.c  cmp_conf.c  sort_conf.c    sort_res.c   id_conf.c\
energies.c  assign_crg.c    coulomb.c   coulomb_conf.c\
get_vdw0.c  get_vdw1.c      relax_water.c      relax_h.c monte.c monte2.c  ran2.c\
relaxation.c collect_connect.c  torsion.c   vdw_fast.c hbond_extra.c swap.c quick_e.c\
check_tpl.c zip.c del_dir.c make_matrices.c\
mem_position.c probe.c add_membrane.c    load_pdb_no_param.c ga_engine.c rotamers_ga.c compute_patches.c

OBJ     = $(SRC:.c=.o)

HEADER  = mcce.h

$(LIB): $(OBJ)
    $(AR) $(ARFLAGS) $(LIB) $(OBJ)

$(OBJ): $(HEADER)

.c.o:
    $(CC) $(CFLAGS) -c $*.c

clean:
    rm -f *.o mcce.a
Run Code Online (Sandbox Code Playgroud)

然后可执行文件本身用这个makefile编译:

CC      = gcc -g -O3
#CC     = icc -xT -static-intel -L/opt/local/lib -L/usr/local/lib

mcce: mcce.c lib/mcce.h lib/mcce.a
#       $(CC) -o mcce mcce.c mcce.a /opt/GDBM/lib/libgdbm.a -lm -lz -openmp; cp mcce bin
        $(CC) -o mcce mcce.c lib/mcce.a -lgdbm -lm -lz -fopenmp; cp mcce bin
Run Code Online (Sandbox Code Playgroud)

我可以使用其他makefile编译其他实验室代码的独立版本:

OEDIR = ../..

INCDIR = $(OEDIR)/include
LIBDIR = $(OEDIR)/lib

INCS = -I$(INCDIR)
LIBS = -L$(LIBDIR) \
    -loezap \
    -loegrid \
    -loefizzchem \
    -loechem \
    -loesystem \
    -loeplatform \
    -lz \
     -lpthread  -lm

CXX = /usr/bin/c++
RM = rm -f
CXXFLAGS = -m64 -W -Wall   -O3 -fomit-frame-pointer -ffast-math 
LFLAGS = -m64 -s

TEXT2HEX = ../text2hex

PROGRAMS = other_labs_code

.SUFFIXES:  .cpp
.SUFFIXES:  .o
.cpp.o:
    $(CXX) $(CXXFLAGS) $(INCS) -c $<

.SUFFIXES:  .txt
.SUFFIXES:  .itf
.txt.itf:
    $(TEXT2HEX) $< InterfaceData > $@

all:        $(PROGRAMS)

clean:  
    $(RM) $(PROGRAMS)
    $(RM) ii_files core a.out *.itf
    $(RM) *.o

other_labs_code.o:  other_labs_code.cpp other_labs_code.itf
other_labs_code:    other_labs_code.o 
    $(CXX) other_labs_code.o $(LFLAGS) -o $@ $(LIBS)
Run Code Online (Sandbox Code Playgroud)

我知道我必须改变各种库和东西的路径,但除此之外,我如何将所有这些makefile合并为一个工作产品?另外,由于编译我的程序主库(mcce.a)的一些源文件需要能够从C++源文件调用函数,所以我需要修改库的makefile,对吧?

我对makefile知之甚少,所以即使有人能指出我的教程方向来解决这类问题(为许多源文件C和C++程序编写一个makefile),这可能就足够了.

对于奖励积分,C++ FAQ说:

编译main()时必须使用C++编译器(例如,用于静态初始化)
您的C++编译器应指导链接过程(例如,它可以获取其特殊库)

我不确切地知道那些东西应该是什么意思,但假设我这样做了,还有其他重要的观点,比如在结合使用C和C++时我应该注意什么?

den*_*ane 6

准备代码

C程序不能只使用C++符号.除非C++代码的作者为此安排.这是因为C++提供的一些功能,例如函数重载(具有多个相同名称但具有不同形式参数的函数)要求以某种方式损坏函数名称.否则链接器将看到多次定义的相同符号.C编译器不理解这个名称,因此不能使用C++符号.通常有两种可能的解决方案.

  1. 声明并定义C代码想要在extern "C" { ... }块中使用的所有C++符号,并让C++工具处理链接.在这种情况下,不需要更改C代码.
  2. 使用与C++代码完全相同的C++编译器编译C代码.修复C++编译器出现C代码的抱怨.根据项目规模和编码风格,这可能是也可能不是很多工作.

准备主Makefile

我个人试图避免与其他人的Makefile密切相关,特别是如果它们易于变化或复杂.因此,假设生成一个Makefile来编排你已经拥有的位(而不是编写一个包含所有内容的Makefile),那么我会从类似的东西开始:

我在假设

  • 已经实施了上述选项之一
  • 代码mcce.a位于子目录中mcce/lib/
  • other_labs_code.cpp 在于 other_labs_code/
  • main你想要使用的功能在于./mystuff.c

以下顶级Makefile可以帮助您入门

CXX = c++
CXXFLAGS = -m64 # From other_labs_code/Makefile
LDFLAGS = -m64 -L<path to OEDIR> # From other_labs_code/Makefile
LIBS = -lgdbm -lm -lz # From mcce/lib/Makefile
LIBS += -loezap \ # From other_labs_code/Makefile
    -loegrid \
    -loefizzchem \
    -loechem \
    -loesystem \
    -loeplatform \
    -lpthread

mystuff: mystuff.c mcce/lib/mcce.a other_labs_code/other_labs_code.o
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)

mcce/lib/mcce.a:
    cd mcce/lib/ && $(MAKE) CC="$(CXX) -m64" mcce.a

other_labs_code/other_labs_code.o:
    cd other_labs_code/ && $(MAKE) other_labs_code.o

Makefile: mcce/lib/Makefile other_labs_code/Makefile
    echo "Warning: `pwd`/$@ is out of date" >&2
Run Code Online (Sandbox Code Playgroud)

这个Makefile将使用现有的子项目Makefile进行编译.如果子项目Makefile具有比此Makefile更新的时间戳,可能使其过时,则会发出警告.链接基本上通过组合两个子项目的所需库来工作.我删除了重复项.编译器开关基本上是原始作者的开关,因为编译被委托给子项目.两个子项目生成的代码必须是针对同一平台的.如果您的编译器是gcc/g ++,那么它们-m64是默认值,因此在第二个项目中是多余的,或者应该添加到第一个项目中.我已经说明将它注入第一个项目而不更改它们的Makefile(使用GNU make).注意:此示例还会导致使用C++编译器编译第一个项目.

extern "C" {...}位于C代码要包含的C或C++头文件中的块应如下所示

/* inclusion guard etc */

#if defined(__cplusplus)
extern "C" {
#endif

/* C declarations */

#if defined(__cplusplus)
}
#endif

/* inclusion guard etc */
Run Code Online (Sandbox Code Playgroud)

小点

在第一个发布的Makefile中,我建议将底部部分更改为

.c.o:
    $(CC) $(CFLAGS) -c -o $@ $<

clean:
    rm -f $(OBJ) mcce.a

.PHONY: clean
Run Code Online (Sandbox Code Playgroud)

这是一个小清洁.

第二个Makefile 坏了.底部规则链接二进制文件,然后将其复制到名为bin的目录(如果存在),否则创建该文件的副本并命名为"bin".如果链接失败,则该事实不会传播给调用者,即忽略错误.最底层的规则应该是

mcce: mcce.c lib/mcce.h lib/mcce.a
    $(CC) -o $@ mcce.c lib/mcce.a -lgdbm -lm -lz -fopenmp
    cp mcce bin/
Run Code Online (Sandbox Code Playgroud)

即link命令应该在它自己的行上,并且`bin'应该是一个目录应该是显式的.