在 Windows 和 Linux 下处理不同源和对象目录的 Makefile

Posted

技术标签:

【中文标题】在 Windows 和 Linux 下处理不同源和对象目录的 Makefile【英文标题】:Makefile dealing with different source and object directories under Windows and Linux 【发布时间】:2015-03-04 18:03:01 【问题描述】:

我在为源文件分散在不同文件夹中的项目创建 Makefile 时遇到问题。由于源文件可能具有相同的名称,我也想将对象发送到不同的文件夹中。使用单个源文件和目标文件,我可以很好地遵循其他地方发布的规则 (Automatic makefile with source and object files in different directories)。 Makefile 必须足以与 Windows 和 Linux 一起使用,我通过 pathfix 定义解决了这个问题(参见下面的代码)。这部分做得很好,但我可能会忽略一些细节。 这是 Makefile 的缩短版本,展示了我的方法:

CXX = g++

CFLAGS = -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp

FILES = main.cpp
FILES_MINIME = minime_class.cpp fifo_class.cpp fit_fun.cpp funcs.cpp gabe_class.cpp minimizer_s.cpp random.cpp

# check the OS
ifdef windir
 binfix = .exe
 pathfix = $(subst /,\,$1)
 OSID = 1
else
 ifeq ($(shell uname), Linux)
  binfix =
  pathfix = $1
  OSID = 0
 else
  ifneq ($(shell uname), Linux)
   $(error OS not identified)
  endif
 endif
endif

# define the different paths of objects and sources
ODIR = obj
SDIR = .
MINIME_SDIR = $(call pathfix, ../minime_pp/src)
MINIME_ODIR = $(call pathfix, obj/minime_pp)

# create templates for the objects by replacing the cpp with an o
_OBJECTS = $(patsubst %.cpp, %.o, $(FILES))
_OBJECTS_MINIME = $(patsubst %.cpp, %.o, $(FILES_MINIME))

# combine the source- and the file names
SOURCE_MINIME = $(call pathfix, $(addprefix $(MINIME_SDIR)/, $(FILES_MINIME)))

# now combine the object- and the file names
OBJECTS = $(call pathfix, $(addprefix $(ODIR)/, $(_OBJECTS)))
OBJECTS_MINIME = $(call pathfix, $(addprefix $(MINIME_ODIR)/, $(_OBJECTS_MINIME)))

# add upp all the objects
OBJECTS_ALL = $(OBJECTS) $(OBJECTS_MINIME)

# define the rules
$(PROJECT): $(OBJECTS_ALL)
    $(CXX) -o $@$(binfix) $^

$(OBJECTS): $(FILES) Makefile
    $(CXX) $(CFLAGS) -c $< -o $@

$(OBJECTS_MINIME): $(SOURCE_MINIME)
    $(CXX) $(CFLAGS) -c $< -o $@

# create the folders (if necessary)
$(OBJECTS): | $(ODIR)

$(OBJECTS_MINIME): | $(MINIME_ODIR)

$(ODIR):
    mkdir $(ODIR)

$(MINIME_ODIR):
    mkdir $(MINIME_ODIR)

问题在输出中最容易说明。在 Windows 上,mingw32-make &gt; output.txt 读取

g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c main.cpp -o obj\main.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\minime_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\fifo_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\fit_fun.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\funcs.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\gabe_class.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\minimizer_s.o
g++ -O0 -g -Wall -Wextra -pedantic -std=c++11 -fopenmp -c ..\minime_pp\src\minime_class.cpp -o obj\minime_pp\random.o

原因是 -c $&lt; 语法只接受依赖项的第一个条目(在本例中为 ..\minime_pp\src\minime_class.cpp)。

解决方案可能是切换回类似的定义

$(MINIME_ODIR)/%.o: $(MINIME_SDIR)/%.cpp
    $(CXX) $(CFLAGS) -c $< -o $@

现在的问题是反斜杠 - $(OBJECTS_ALL) 中没有要求匹配这个新定义,因此没有任何反应。在定义中使用\ 将不起作用。所以我认为你摆脱了反斜杠 - 正斜杠转换,但是自动目录创建在 Windows 上不起作用。类似-c $&lt;$&lt; 从没有/%.cpp 的列表中选择“匹配”的基本上是我正在寻找的东西。

如果有人知道编写两个 Makefile(Windows、Linux)的方法,感谢您的提示。

【问题讨论】:

为什么不在路径中使用\` in the target and prerequisite work? Are you sure you need the pathfix stuff at all? (Windows should handle /` 在大多数地方都很好。) 大多数地方,是的,但不是mkdir 因此,只需使用 pathfix 执行该命令(MadScientist 击败了我)。 使用msys2.org 在 Windows 中进行构建,它将使您免于许多 Windows 问题。另外,请考虑将github.com/cppfw/prorab 与makefiles 一起使用,因为它已经解决了许多问题,包括不同目录中的同名源文件。 【参考方案1】:

你不应该在 makefile 中使用反斜杠分隔符。几乎所有 Windows 工具都支持普通的 UNIX 斜杠作为目录分隔符。对于那些不这样做的人来说,将它们转换为配方中的特定实用程序会更容易:换句话说,只在需要它们的地方调用$(pathfix ...)

请注意,几乎所有需要反斜杠的命令都是特定于 Windows (command.com) 的命令,无论如何都不能移植到 UNIX。

【讨论】:

谢谢,我在大多数地方删除了pathfix,除了mkdircall(以及其他地方)。然后遵循通配符 (%) 方法就可以了。【参考方案2】:

我这样做是为了让 Windows 和 Linux/MacOS 都使用相同的 makefile:

ifeq ($(OS),Windows_NT)
    RM = del /Q /F
    RRM = rmdir /Q /S
    mkdirp = mkdir $(subst /,\,$1)
else
    RM = rm -f
    RRM = rm -rf
    mkdirp = mkdir -p $1
endif

$(BUILDDIR):
    $(call mkdirp, $@)

clean:
    -$(RRM) $(BUILDDIR)
    -$(RM) $(TARGET)

【讨论】:

以上是关于在 Windows 和 Linux 下处理不同源和对象目录的 Makefile的主要内容,如果未能解决你的问题,请参考以下文章

JAVA:不同源文件夹下存在相同包和类名的冲突

windows和Linux下定时启动或关闭服务

如何在 Safari(Windows 7)中禁用同源策略?

Windows Socket和Linux Socket编程有什么区别

在linux、mac和windows中使用JNI

同源和跨域问题及解决方案