本人是windows开发环境,最近在搭建Eclipse CDT环境编译RiscV环境,然后使用了Eclipse的build tools环境,奇怪的是本地有很多makefile环境的c代码,会编译异常。
为了专门做了一些实验进行测试。
本例程所有相关代码在GitHub上,请自行下载。bobwenstudy/test_makefile_shell_env (github.com)
为了说明发生什么,特意写了一个测试程序,以便对此问题进行分析。
main.c
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
Makefile
,通过TEST_OS
来区分不同测试环境,第8行打印当前的SHELL
和ComSpec
配置。
通过修改SHELL
配置,即可调整当前的执行环境。
OUTPUT_DIR = output/obj
OBJECTS = $(OUTPUT_DIR)/main.o
MKD := mkdir
RM := del /q /s
FIXPATH = $(subst /,\,$1)
$(warning SHELL, $(SHELL), $(ComSpec))
ifeq ($(TEST_OS),1)
MKD := mkdir -p
RM := rm -rf
FIXPATH = $1
endif
ifeq ($(TEST_OS),2)
ifdef ComSpec
WINCMD:=$(ComSpec)
endif
ifdef COMSPEC
WINCMD:=$(COMSPEC)
endif
SHELL:=$(WINCMD)
MKD := mkdir -p
RM := rm -rf
FIXPATH = $1
endif
ifeq ($(TEST_OS),3)
ifdef ComSpec
WINCMD:=$(ComSpec)
endif
ifdef COMSPEC
WINCMD:=$(COMSPEC)
endif
SHELL:=$(WINCMD)
MKD := mkdir
RM := del /q /s
FIXPATH = $(subst /,\,$1)
endif
ifeq ($(TEST_OS),4)
ifdef ComSpec
WINCMD:=$(ComSpec)
endif
ifdef COMSPEC
WINCMD:=$(COMSPEC)
endif
SHELL:=$(WINCMD)
MKD := "mkdir" -p
RM := rm -rf
FIXPATH = $1
endif
ifeq ($(TEST_OS),5)
ifdef ComSpec
WINCMD:=$(ComSpec)
endif
ifdef COMSPEC
WINCMD:=$(COMSPEC)
endif
SHELL:=$(WINCMD)
MKD := mkdir.exe -p
RM := rm -rf
FIXPATH = $1
endif
all: $(OBJECTS)
gcc $^ -o $(OUTPUT_DIR)/main
$(OBJECTS): $(OUTPUT_DIR)/%.o: %.c
$(MKD) $(call FIXPATH, $(@D))
gcc -c -o $@ $<
clean:
$(RM) $(call FIXPATH, $(OUTPUT_DIR))
如下所示,环境只有一个mingw32的make环境。
创建目录:MKD := mkdir
删除目录:RM := del /q /s
调整路径:FIXPATH = $(subst /,\,$1)
进行make all
和make clean
操作,程序都按照预期进行。
创建目录:MKD := mkdir -p
删除目录:RM := rm -rf
调整路径:FIXPATH = $1
进行make TEST_OS=1 all
和make TEST_OS=1 clean
操作,程序都执行失败。mkdir失败是因为路径分隔符不对。rm是没有这个执行文件。
这些行为都是符合我预期的,一般我会通过OS
判断来区分当前的系统,而后选择不同的执行命令。
到CDT的官网Eclipse Embedded CDT | Eclipse Embedded CDT (C/C++ Development Tools)™ (eclipse-embed-cdt.github.io),可以看到其有推荐windows的build tool环境:The xPack Windows Build Tools | The xPack Build Framework
直接下载就行,本地已经下载好了一份,放在路径下build-tools
。
为了方便复现,执行setup.bat脚本来进行环境配置。成功配置后,where make可以看出环境是ok的。
创建目录:MKD := mkdir
删除目录:RM := del /q /s
调整路径:FIXPATH = $(subst /,\,$1)
进行make all
和make clean
操作,程序都报错了,可以看到这时make的SHELL
已经指向了build-tools目录下的sh.exe,这时候程序已经工作在linux环境下了,不能再使用windows的mkdir/del行为了。
创建目录:MKD := mkdir -p
删除目录:RM := rm -rf
调整路径:FIXPATH = $1
进行make TEST_OS=1 all
和make TEST_OS=1 clean
操作,程序都执行成功了。整个环境就是linux的环境了。
创建目录:MKD := mkdir -p
删除目录:RM := rm -rf
调整路径:FIXPATH = $1
进行make TEST_OS=2 all
和make TEST_OS=2 clean
操作,mkdir
执行失败了,rm确没报错。因为这时候rm.exe
已经在build-tools
有提供了,那为什么这时候没有使用build-tools
目录下的mkdir.exe
呢?
创建目录:MKD := mkdir
删除目录:RM := del /q /s
调整路径:FIXPATH = $(subst /,\,$1)
进行make TEST_OS=3 all
和make TEST_OS=3 clean
操作,这时候又都执行成功了。
从上面的测试程序可以看到一个情况,当使用CDT的build-tools
时,程序make行为不确定了。测试程序中,我们是通过setup.bat
来配置PATH参数,只影响当前编译环境,但是通常我们会直接配置系统的环境变量PATH
。这时系统make环境可能就出现错乱了,本人就遇到了这个问题。
在使用CDT工具链情况下,SHELL指定为windows时,由于这时候mkdir和windows环境的命令相同(Windows 命令 | Microsoft Learn),windows命令的优先级更高。
通过双引号""
强制指定不使用Windows的命令,而是用build-tools中提供的mkdir.exe。由于rm没冲突,所以不需要加调整。
创建目录:MKD := "mkdir" -p
删除目录:RM := rm -rf
调整路径:FIXPATH = $1
进行make TEST_OS=4 all
和make TEST_OS=4 clean
操作,mkdir
和rm
都能正常执行了。
通过.exe
强制指定不使用Windows的命令,而是用build-tools中提供的mkdir.exe。由于rm没冲突,所以不需要加调整。
创建目录:MKD := mkdir.exe -p
删除目录:RM := rm -rf
调整路径:FIXPATH = $1
进行make TEST_OS=5 all
和make TEST_OS=5 clean
操作,mkdir
和rm
都能正常执行了。
本质CDT提供的是linux下的build环境,当将CDT的build-tools作为默认编译环境时,就会出现make操作执行时,认为是linux环境,当想进行mkdir操作是,会和windows命令冲突,这时候如果系统中还有别的工程时,就会有冲突了。
只要理解本质原因,就可以根据各种情况针对性处理了。