在Linux上使用makefile,使用Boost和MPI对"main"的未定义引用

undefined reference to `main' with makefile on linux, with boost and mpi

本文关键字:MPI main 引用 未定义 Boost Linux makefile 使用      更新时间:2023-10-16

我对make不是很熟悉;我一直在使用和修改这个从另一个项目继承的非常古老的makefile,并在必要时对其进行修改。到目前为止,它的功能一直很好,将20-50个文件的项目编译在一起,甚至在子目录中,正确地确定和构建所有依赖项。

今天我运行一个非常简单的测试代码来确定是否BOOST。MPI将在我最近访问的一个集群上运行。我正在尝试编译一个1文件的测试用例,以确保该库正常工作。这也是我第一次构建并链接到这个系统。

我在用户目录~/boost中安装了boost,并运行了适当的bjam以确保boost。MPI存在。我不相信我的问题就在那里。但是,稍后您将看到,我必须包含-lbox_mpi-labox_serialization才能达到我所做的程度。

makefile似乎是编译的,并且组装得很好,生成了中介.a文件。它挂断了链接步骤,出现了一个令人惊讶的错误:

Creating dependency for "boosttest.cpp"
Compiling "boosttest.cpp"
Assembling ag...
ar: creating ./libag.a
Linking bt1...
/usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status
make: *** [bt1] Error 1

我写的唯一源代码(直接从Boost.MPI实现页面复制/粘贴):

boosttest.cpp:

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <iostream>

int main(int argc, char* argv[])
{
  boost::mpi::environment env(argc, argv);
  boost::mpi::communicator world;
  std::cout << "I am process " << world.rank() << " of " << world.size()<< "." << std::endl;
  return 0;
}

现在是我迷路的部分。这是我修改过的makefile。我基本上了解它的各个部分在做什么,它是我见过的唯一一个生成.d文件的makefile(尽管如前所述,我对它们没有太多经验),该文件包含所有依赖库的列表。

生成文件:

PROJECT = boosttest
# The names of the sub-projects that the  executable depends on.  These names are used later on to define targets, location, libraries, etc.
DEPS =
# The names of the sub-projects that the  test executable depends on.
TEST_DEPS =
EXE_DEPS =
LOCAL_O_FILES       = boosttest.o
LOCAL_TEST_O_FILES  =
LOCAL_LIB_NAME      = ag
LOCAL_LIB_DIR       = .
LOCAL_TEST_LIB_NAME =
LOCAL_TEST_LIB_DIR  =
LOCAL_INCLUDE_ROOT  = .
EXE_NAME            = BT

MPIC_DIR = /usr/local/mvapich2-1.6-gcc/
#these assume our local path and bin are set up properly since I don't know where the compilers are
CC          = $(MPIC_DIR)/bin/mpicc
CCC         = $(MPIC_DIR)/bin/mpicxx
F77         = $(MPIC_DIR)/bin/mpif77
CLINKER     = $(MPIC_DIR)/bin/mpicc
CCLINKER    = $(MPIC_DIR)/bin/mpicxx
FLINKER     = $(MPIC_DIR)/bin/mpif90
F90         = $(MPIC_DIR)/bin/mpif90
F90LINKER   = $(MPIC_DIR)/bin/mpif90
MAKE        = make --no-print-directory
SHELL       = /bin/sh

#PROF = -ggdb -O2
PROF = -O2
ADDITIONAL_LIBS = -lboost_mpi -lboost_serialization
SED  = $(shell which sed)
GREP = $(shell which grep)
# Warnings will depend on the GCC version -- if it's 4, have "-Wdeclaration-after-statement -Wunused-value -Wunused-variable"
#GCC_MAJOR_VERSION = $(shell $(CCC) --version | $(GREP) "(egcs|gcc)" | $(SED) "s/[^0-9]*([0-9]).*/1/")
#WARNINGS = -Wno-import $(shell if test $(GCC_MAJOR_VERSION) -eq 4; then echo -Wunused-value -Wunused-variable; fi)
#GCC_INSTALL_DIR = /turing/software-linux/mpich-eth
GCC_INSTALL_DIR = $(MPIC_DIR)
GCC_INCLUDE_DIR = $(GCC_INSTALL_DIR)/include/
GCC_LIB_DIR = $(GCC_INSTALL_DIR)/lib/
BOOST_DIR = ~/boost
BOOST_LIB_DIR = ~/boost/stage/lib
# Expand SRCDIR so that it turns into "-I <srcdir>" for each listed directory
INCLUDE_DIRS = -I $(OBJC_ROOT)/include $(foreach dep, $(UNIQUE_DEPS), -I$($(dep)_INCLUDE_ROOT)) -I $(LOCAL_INCLUDE_ROOT) -I $(BOOST_DIR) -I $(GCC_INCLUDE_DIR)
#C_FLAGS  = -DROM $(PROF) $(WARNINGS) $(INCLUDE_DIRS)
C_FLAGS = -DROM $(PROF) $(INCLUDE_DIRS)
L_FLAGS  = $(foreach dir, $(OBJC_ROOT) $(DEP_LIB_DIRS), -L$(dir)) -L $(BOOST_LIB_DIR) -L $(GCC_LIB_DIR) $(PROF) $(ADDITIONAL_LIBS)
TEST_L_FLAGS  = $(foreach dir, $(OBJC_ROOT) $(TEST_DEP_LIB_DIRS), -L$(dir)) -L/usr/lib  -Xlinker --whole-archive $(TEST_REQUIRED_LIBS) -Xlinker --no-whole-archive
REQUIRED_LIBS = $(foreach dep, $(DEPS) LOCAL, $(if $($(dep)_LIB_NAME), -l$($(dep)_LIB_NAME)))
TEST_REQUIRED_LIBS = $(foreach dep, $(TEST_DEPS) LOCAL LOCAL_TEST, $(if $($(dep)_LIB_NAME), -l$($(dep)_LIB_NAME)))
.SUFFIXES:
.SUFFIXES: .d .cc .cpp .c .o
ASSEMBLE_TARGETS = $(foreach dep,$(DEPS),$(dep)_LIB_NAME)
ASSEMBLE_TEST_TARGETS = $(foreach dep,$(TEST_DEPS),$(dep)_LIB_NAME)
CLEAN_TARGETS = $(foreach dep, $(UNIQUE_DEPS), $(dep)_CLEAN)
LOCAL_D_FILES = $(LOCAL_O_FILES:.o=.d)
LOCAL_TEST_D_FILES = $(LOCAL_TEST_O_FILES:.o=.d) $(TEST_RUNNER:.o=.d)
DEP_LIB_DIRS = $(foreach dep, $(DEPS) LOCAL, $($(dep)_LIB_DIR))
TEST_DEP_LIB_DIRS = $(foreach dep, $(TEST_DEPS) LOCAL LOCAL_TEST, $($(dep)_LIB_DIR))
UNIQUE_ASSEMBLE_TARGETS = $(sort $(ASSEMBLE_TARGETS) $(ASSEMBLE_TEST_TARGETS))
UNIQUE_DEPS = $(sort $(DEPS) $(TEST_DEPS))
LOCAL_LIB = $(if $(LOCAL_LIB_NAME), $(LOCAL_LIB_DIR)/lib$(LOCAL_LIB_NAME).a)
LOCAL_TEST_LIB = $(if $(LOCAL_TEST_LIB_NAME), $(LOCAL_TEST_LIB_DIR)/lib$(LOCAL_TEST_LIB_NAME).a)
EXE_TARGETS = $(foreach dep,$(EXE_DEPS),$(dep)_EXE_NAME)
INSTALL_TARGETS = $(foreach dep,$(EXE_DEPS),$(dep)_INSTALL)
export PROJECTS += $(PROJECT)
# PHONY targets get remade even if there exists an up-to-date file with the same name.
.PHONY: default clean compile assemble link submit
# Targets for mortals
default: link
clean: $(CLEAN_TARGETS) LOCAL_CLEAN
compile: $(DEPS) $(LOCAL_D_FILES) $(LOCAL_O_FILES)

assemble: compile $(ASSEMBLE_TARGETS) $(LOCAL_LIB)
link: assemble $(EXE_TARGETS) $(EXE_NAME)

install: $(INSTALL_TARGETS) $(if $(EXE_NAME), LOCAL_INSTALL)
submit: link
        qsub $(PROJECT).qsub

# Targets for make
# Invoking sub projects
$(UNIQUE_DEPS): MAKE_TARGET = $(if $(filter %_TEST, $@), compile-tests, compile)
$(UNIQUE_DEPS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($@_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _LIB_NAME attached by ASSEMBLE_TARGETS, a
$(UNIQUE_ASSEMBLE_TARGETS): DEP_NAME = $(@:_LIB_NAME=)
$(UNIQUE_ASSEMBLE_TARGETS): MAKE_TARGET = $(if $(filter %_TEST, $(DEP_NAME)), assemble-tests, assemble)
$(UNIQUE_ASSEMBLE_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _EXE_NAME attached by EXE_TARGETS, a
$(EXE_TARGETS): DEP_NAME = $(@:_EXE_NAME=)
$(EXE_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) link)
$(CLEAN_TARGETS): DEP_NAME = $(@:_CLEAN=)
$(CLEAN_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) clean)
$(INSTALL_TARGETS): DEP_NAME = $(@:_INSTALL=)
$(INSTALL_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) install)
#Local stuff
# The rule to change either a '.c' or a '.m' to a '.o'
#%.o : %.c %.d %.cc %.cpp
.cc.o .cpp.o .c.o:
        @echo "Compiling "$<""
        @$(CCC) -c $(C_FLAGS) $< -o $@
.cc.d .cpp.d .c.d :
        @echo "Creating dependency for "$<""
#       @$(CCC) $(WARNINGS) $(INCLUDE_DIRS) -MM $< -o $@
# This foogly hack because gcc seems to have issues with emitting the correct target/dependency names of
# files in sub-dirs of the current dir (specifically, it doesn't add the sub-dir to the target
# name, and insists on adding the directory to the dependencies) which ends up breaking dependencies...)
        @dependLine=$$( $(CCC) $(C_FLAGS) $(INCLUDE_DIRS) -MM $< ); 
        dirName=$$( dirname $< | $(SED) "s///\\//g" ); 
        dependLine=$$( echo $${dependLine} | $(SED) "s/ $${dirName}// /g" ); 
        oFile=$$( echo $${dependLine} | $(SED) "s/:.*//" ); 
        dependencies=$$( echo $${dependLine} | $(SED) "s/.*://" ); 
        echo $${oFile} $${oFile%.o}.d: $${dependencies} | $(SED) "s/ \//g" > $@
$(UNIQUE_ASSEMBLE_TARGETS): DEP_NAME = $(@:_LIB_NAME=)
$(UNIQUE_ASSEMBLE_TARGETS): MAKE_TARGET = $(if $(filter %_TEST, $(DEP_NAME)), assemble-tests, assemble)
$(UNIQUE_ASSEMBLE_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _EXE_NAME attached by EXE_TARGETS, a
$(EXE_TARGETS): DEP_NAME = $(@:_EXE_NAME=)
$(EXE_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) link)
$(CLEAN_TARGETS): DEP_NAME = $(@:_CLEAN=)
$(CLEAN_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) clean)
$(INSTALL_TARGETS): DEP_NAME = $(@:_INSTALL=)
$(INSTALL_TARGETS):
        $(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) install)
#Local stuff
# The rule to change either a '.c' or a '.m' to a '.o'
#%.o : %.c %.d %.cc %.cpp
.cc.o .cpp.o .c.o:
        @echo "Compiling "$<""
        @$(CCC) -c $(C_FLAGS) $< -o $@
.cc.d .cpp.d .c.d :
        @echo "Creating dependency for "$<""
#       @$(CCC) $(WARNINGS) $(INCLUDE_DIRS) -MM $< -o $@
# This foogly hack because gcc seems to have issues with emitting the correct target/dependency names of
# files in sub-dirs of the current dir (specifically, it doesn't add the sub-dir to the target
# name, and insists on adding the directory to the dependencies) which ends up breaking dependencies...)
        @dependLine=$$( $(CCC) $(C_FLAGS) $(INCLUDE_DIRS) -MM $< ); 
        dirName=$$( dirname $< | $(SED) "s///\\//g" ); 
        dependLine=$$( echo $${dependLine} | $(SED) "s/ $${dirName}// /g" ); 
        oFile=$$( echo $${dependLine} | $(SED) "s/:.*//" ); 
        dependencies=$$( echo $${dependLine} | $(SED) "s/.*://" ); 
        echo $${oFile} $${oFile%.o}.d: $${dependencies} | $(SED) "s/ \//g" > $@
$(LOCAL_LIB): compile $(ASSEMBLE_TARGETS)
        @echo Assembling $(LOCAL_LIB_NAME)...
        @ar rs $(LOCAL_LIB) $(LOCAL_O_FILES)
# Create the executable
$(EXE_NAME): assemble
        @echo Linking $(EXE_NAME)...
        @$(CCC) -o $(EXE_NAME) $(L_FLAGS)
# Erase all object files, the dependencies file, the core file and the executable, then rebuild everything
LOCAL_CLEAN:
        @echo Cleaning $(PROJECT)...
        @rm -f $(LOCAL_O_FILES) $(LOCAL_TEST_O_FILES) $(LOCAL_LIB) $(LOCAL_TEST_LIB) $(EXE_NAME) $(TEST_EXE_NAME) $(LOCAL_D_FILES) $(LOCAL_TEST_D_FILES) $(TEST_RUNNER) $(TEST_RUNNER:.o=.d) core*

ifeq (,$(findstring clean,$(MAKECMDGOALS)))
-include $(LOCAL_O_FILES:.o=.d) $(LOCAL_TEST_O_FILES:.o=.d)
endif

我的mpicxx编译器的路径(集群需要,对于那些不熟悉的人,它包装gcc)是正确的,这可以从它确实编译的事实中证明,它只是不会链接。类似地,我的boost库路径似乎是有效的,-lbox_mpi标志被正确捕获。

在这一点上,我的问题已经扩展到我自己的教育,基于这个makefile是如何工作的。最终,完全显而易见/简单的命令:

mpicxx boosttest.cpp -L ~/boost/stage/lib/ -lboost_mpi -lboost_serialization -o test

然而,我想得到一个合适的makefile,就像我通过这个测试用例验证Boost一样。MPI相当于一个hello世界,我将继续进行涉及我的完整源代码、20多个具有交叉依赖关系的文件等的项目。任何帮助或直觉都将不胜感激。

根据@Oktalist的建议,我能够找到一些有效的东西,至少对于单个文件来说是这样,但我不明白为什么当我将其扩展到多个文件时它不起作用。我仍然不知道为什么这在过去有效,但现在不行,但也许我改变了一些我没有跟踪的东西。

校正已接近尾声,在此块中:#创建可执行文件$(EXE_NAME):组装@echo正在链接$(EXE_NAME)。。。@$(CCC)$(L_FLAGS)$(LOCAL_LB)-o$(EXE_NAME)