【PE】搭建支援C99原始碼編譯的vs2010工程的方法(附MinGW下Windows GNU makefile的編寫)
DATE: 2018.12.10
1、前言
最近在編譯一份開原始碼時,由於VS對最新標準C實現C99的支援性差,在搭建編譯環境過程中遇到了一些問題,特記錄於此。
現在很多開原始碼(比如x264,ffmpeg)中的C程式碼都採用c99規範,Linux gcc編譯器對c99目前也不是完全支援,但可以通過指定引數-std=c99或-std=gnu99來使用c99規範;VS對c99的支援性就更差了,目前vs2010,vs2012都不支援c99規範,從vs2013開始才部分支援c99規範。因此,問題來了:
如果我們需要vs2010版本編譯的可執行檔案或庫怎麼辦呢?
目前主要有一種解決方案:
採用c99toc89轉換工具進行預處理然後送入編譯器進行編譯。
2、參考
C99與C89區別以及轉換方法
C編譯標準-std=的設定方法以及工程標頭檔案包含設定
C99中stdint.h和inttype.h標頭檔案的使用方法及獲取路徑
3、搭建支援C99原始碼編譯的vs2010工程的方法
採用c99toc89工具:Tool to convert C99 code to MSVC-compatible C89
Usage: c99wrap $CC $CFLAGS source
3.1、vs2010工程中使用自定義生成工具搭建(這種方式便於除錯分析)
將工程中所有c檔案和cpp檔案設定自定義生成工具如下:
c99wrap cl / c /Fo "Debug\%(Filename).obj" %(FullPath)
輸出設定為:
Debug\%(Filename).obj;%(Outputs)
注意: 當前測試發現,在純VS環境下,使用c99wrap轉換工具後編譯c99程式碼仍然會報錯!
原因: c99wrap.exe工具引數配置有問題導致(c99wrap工具引數解析採用’-c’的形式!),
正確的引數配置如下:
c99wrap cl -c -Fo "Debug\%(Filename).obj" %(FullPath)
Debug\%(Filename).obj;%(Outputs)
舉個栗子:
c99toc89_test.project//demo.c:
#include <stdio.h>
int add(int a, int b)
{
int sum = 0;
for(int i = 0; i< 10; i++)
{
sum += a + b;
}
return sum;
}
int main(int argc, char* argv[])
{
int ret;
ret = sum(5, 5);
return ret;
}
上述程式碼中for語句採用了c99語法中的for語句內進行變數宣告。
編譯結果如下圖所示:
vs2010下需要使用c99toc89工具先轉換一下:
vs2013下可以直接編譯通過:
3.2、MinGW環境中利用C99wrap採用編譯指令碼生成vs2010版本的可執行檔案(指令碼編譯)
強烈建議採用最新版本的c99wrap(V1.0.3),對C99基本完全支援。
下載地址:https://github.com/libav/c99-to-c89/releases/tag/release-1.0.3
(1)編譯環境:
- 首先在啟動MinGW環境之前,修改以下指令碼(路徑:MinGW/msys/1.0/msys.bat),並更名為msys_vs2010_win32/64.bat;
在該指令碼的最開始新增下面的語句,啟動VS執行環境。
rem 對於win32:
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat"
rem 對於x64:
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\vcvars64.bat"
- 啟動MinGW環境,執行msys_vs2010_win32.bat或msys_vs2010_win64.bat。
(2)編譯指令碼:
platform.rules:
OS = $(shell uname)
CFLAGS += $(EXTRA_CFLAGS)
ifeq($(findstring MINGW, $(OS)),MINGW)
CC= c99wrap cl
CPP= c99wrap cl
AR= lib
LD= c99wrap link
ASM= yasm
ifeq($(PLATFORM), x86_32)
CFLAGS += -DWIN32 -DARCH_X86_32 -O2
SFLAGS += -f win32 -DPREFIX
RC = RC -DWIN32
endif
ifeq($(PLATFORM), x86_64)
CFLAGS += -DWIN64 -DARCH_X86_64 -O2
SFLAGS += -f x64 -DPREFIX
RC = RC -DWIN64
endif
endif
Makefile_lib:
include platform.rules
SRC_DIR = ./src
SRCS := $(wildcard $(SRC_IDR)/*c)
OBJS := $(subst .c,.o, $(SRCS))
TARGET= libxxx.lib dllxxx.dll
DEFS = xxxx.def #模組定義檔案def
IMPLIB= dllxxx.lib #引導庫
VER = xxx.ver
PDB_NAME = libxxx.pdb
OUT = -out
LIBRARY_STATIC = $(OUT):$(filter %.lib, $TARGET))
LIBRARY_SHARD = $(OUT):$(filter %.dll, $TARGET))
all: $(TARGET)
$(filter %.lib, $(TARGET)) : $(OBJS)
$(AR) $(LIBRARY_STATIC) $(OBJS)
$(filter %.dll, $(TARGET)) : $(OBJS)
makedef $(VER) $(OBJS) > $(DEFS)
$(RC) -fo enc_dll.res enc_dll.rc
$(LD) -dll -def:$(DEFS) -implib: $(IMPLIB) $(LIBRARY_SHARD) $(OBJS) enc_dll.res $(OUTPDB)
%.o:%.c
$(CC) -I. -I$(SRC_DIR) $(CFLAGS) $< -o [email protected]
%.o:%asm
$(ASM) $(SFLAGS) $< -o [email protected]
clean:
rm -f $(OBJS) $(TARGET) $(IMPLIB) $(PDB_NAME)
Makefile_demo:
include platform.rules
SRC_DEMO_DIR = ../../../demo
vpath %.c $(SRC_DEMO_DIR)
vpath %.h $(SRC_DEMO_DIR)
SRCS := $(wildcard $(SRC_DEMO_DIR )/*c)
C_OBJS := $(subst .c,.o, $(SRCS))
OBJS := $(filter-out ../../demo/osal.o, $(C_OBJS))
TARGET= $(BIN_DIR)/xxx.exe
LIBXXX := $(LIB_DIR)/libxxx.lib
OUT = -out
CFLAGS += -I $(SRC_DEMO_DIR ) -I../../../include _DWIN32
LDFLAGS := -libpath:$(LIB_DIR)
all: clean $(LIBXXX) $(TARGET)
$(TARGET):$(OBJS)
$(LD) $(LDFLAGS) -PDB:$(BIN_DIR)/demo.pdb -debug -out [email protected] $(LIBXXX) $(OBJS) > out
%.o:%.c:
$(CC) $(CLFAGS) -c -Fo [email protected] $<
$(LIBXXX):
make -C ../lib -f Makfile_lib
clean:
rm -rf $(C_OBJS) $(TARGET)
make -C ../liib -f Makfile_lib clean
(3)編譯方法
cd build/demo
make -f Makefile_demo
cd build/lib
make -f Makefile_lib
(4)擴充套件知識
- makedef:庫符號匯出利用了MinGW bin中自帶的makedef工具,使用方法很簡單,VER檔案格式如下:
LIBXXX {
global: XX_MPEG_*
local: *;
};
- filter與filter-out
$(filter ^lib, $(OBJS)) #從$(OBJS)中找到以lib開頭的檔案
$(filter-out test.o, $(OBJS)) #從$(OBJS)中濾除掉test.o檔案
4、採用GnuWin32工具進行windows下gnu makefile編寫
參考:
GnuWin32使用以及windows下gnu makefile編寫