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

发布于 2024-10-23 23:31:15 字数 3533 浏览 6 评论 0原文

我在计算生物物理实验室工作。我不是一名程序员,尽管我确实因为表现得像程序员而获得报酬。我的问题是:实验室的主要产品是一个巨大的(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

然后,可执行文件本身使用此 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

我可以获得其他实验室代码的独立版本,以使用此其他 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)

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

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

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

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

我不完全知道这些东西应该意味着什么,但假设我这样做了,还有其他重要的事情吗?知道何时结合 C 和 C++ 吗?

I work in a computational biophysics lab. I am not a programmer, although I do get paid to act like one. Here's my problem: the main product of the lab is a ginormous (50+ source files) C program. I need to get our lab's program to work with another lab's toolkit, which just so happens to be in the form of a series of C++ libraries (.a files). I can get the main library for our program to compile using the following 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

The executable itself then compiles with this 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

I can get a standalone version of the other lab's code to compile using this other 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 
lt;

.SUFFIXES:  .txt
.SUFFIXES:  .itf
.txt.itf:
    $(TEXT2HEX) 
lt; 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)

I know that I have to change the paths of the various libs and stuff, but other than that, how do I combine all of these makefiles into one working product? Also, since some of the source files that go into compiling my program's main library (mcce.a) are going to need to be able to call functions from the C++ source file, it's the library's makefile that I need to modify, right?

I know extremely little about makefiles, so even if someone can just point me in the direction of a tutorial that covers this kind of problem (writing a makefile for a many source file C and C++ program), that may be sufficient.

For bonus points, the C++ FAQ says that:

You must use your C++ compiler when compiling main() (e.g., for static initialization)
Your C++ compiler should direct the linking process (e.g., so it can get its special libraries)

I don't exactly know what those things are supposed to mean, but assuming that I did, are there any other important points like that I should be aware about when combining C and C++?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

败给现实 2024-10-30 23:31:15

准备代码

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

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

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

/* inclusion guard etc */

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

/* C declarations */

#if defined(__cplusplus)
}
#endif

/* inclusion guard etc */

要点

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

.c.o:
    $(CC) $(CFLAGS) -c -o $@ 
lt;

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

.PHONY: clean

是一个稍微干净一点的。

第二个 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/

的,即链接命令应该在自己的行上,并且“bin”应该是一个目录,并且应该明确表示。

Preparing the code

C programs cannot just use C++ symbols. Unless the authors of the C++ code arranged for that. This is because some features that C++ offers, such as function overloading (having several functions of the same name but with different formal arguments) demand that the function name be mangled in some way. Else the linker would see the same symbol defined several times. C compilers don't understand this name mangling and therefore cannot use C++ symbols. There are, generally, two possible solutions.

  1. Declare and define all C++ symbols that the C code wants to use within extern "C" { ... } blocks and let your C++ tools handle the linking. The C code does not need to be changed in this case.
  2. Compile the C code with the (exact same) C++ compiler as the C++ code. Fix the C++ compiler's complaints of the C code as they arise. Depending on project size and coding style, this may or may not be a lot of work.

Preparing a master Makefile

I personally try to avoid becoming intimate with other people's Makefiles, especially if they are subject to change or complex. So, assuming generating a Makefile that orchestrates the bits you already have (as opposed to writing one Makefile incorporating everything) is okay, I'd start out with something similar to this:

I'm assuming that

  • One of the above-mentioned options has been implemented
  • The code for mcce.a lies in subdirectory mcce/lib/
  • other_labs_code.cpp lies in other_labs_code/
  • The main function you want to use lies in ./mystuff.c

the following top-level Makefile may get you started

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

This Makefile will employ the existing sub-project Makefiles to do the compilations. If the sub-project Makefiles have a newer timestamp than this Makefile, potentially rendering it obsolete, then this will be warned about. The linking basically works by combining the required libraries of both sub-projects. I've removed duplicates. The compiler switches are basially those of the original authors since compiling is delegated to the sub-projects. The code both sub-projects generate must be for the same platform. If your compiler is gcc/g++ then either -m64 is the default and therefore redundant in the second project or should be added to the first project. I have illustrated injecting it into the first project without changing their Makefile (using GNU make). NB: This example also causes the first project to be compiled with the C++ compiler.

An extern "C" {...} block located in a C or C++ header file that C code wants to include should look like this

/* inclusion guard etc */

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

/* C declarations */

#if defined(__cplusplus)
}
#endif

/* inclusion guard etc */

Minor points

In the first posted Makefile, I suggest changing the bottom part to

.c.o:
    $(CC) $(CFLAGS) -c -o $@ 
lt;

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

.PHONY: clean

which is a tiny bit cleaner.

The second Makefile is broken. The bottom rule links the binary and then copies it to a directory named bin, if it exists, else a copy of the file is created and named `bin'. If the linking fails, that fact is not propagated to the caller, i.e. the error is ignored. The bottom rule should read

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

i.e. the link command should be on its own line and that `bin' is supposed to be a directory should be made explicit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文