把已有项目转换成Visual Studio的解决方案

把现存项目变成vs的.sln

  • 背景
  • 实现思路
  • 修改文件系统层面
    • 修改git忽略配置
    • 创建VS解决方案
    • 修正dogt-dll项目的位置
  • 编码,修正环境变化带来的问题
    • 添加附加包含目录
    • 试着编译却失败
    • 解决编码警告
    • 指针预判空
    • 不安全代码替换
  • 引用外部库
  • 总结

背景

现在是这样,有头上有个代码量还算行的小项目,一直用开源工具链gcc/g++和make管理,但最近发现make总是出一些奇怪的错误,要么打印错位,要么我粗心大意搞错文件名之类的,给自己带来了不少麻烦.

因此,既然安装了Visual Studio 2022RC,肯定要发挥用处对不对,在上述要求下,只得在项目里想办法插入vs的管理,但同时保存原有管理方案,以便换机没有vs的环境仍然能简便的安装MinGW-w64来编译项目.

实现思路

学习过python3.10.2源码后,我们知道有一个PCBuild文件夹,里面储存.sln(解决方案文件,也就是项目的根)和.vcxproj文件(所谓的"项目",其实是相对于"解决方案"的一个个组件罢了)

那我们也可以在原有项目下添加一个VSSolution目录,分别存储vs管理整个项目的所需文件,并且添加到版本控制(具体添加那些目录和文件后文详细说明),然后把原有文件添加到proj里面,这样就能然vs用MSVC的编译器了.而且免去很多维护麻烦,比如最近强迫症重命名一个文件,把xxx/A.c改成xxx/B.c,忘记修改Makefile,半天说"no rule to make xxx.o,needed by …"之类的.

因此,如果用vs管理,不仅享受更好的代码优化和警告控制安全性,而且更方便维护项目进展,可谓一举N得

修改文件系统层面

下面步入正题.我的项目地址在这里 (或许你们访问的时候已经修改好了呢)

├─.vscode
├─bin
├─build
├─builtin
├─documents
│  ├─bte
│  ├─rte
│  └─wrn
├─dogc
│  ├─common
│  ├─errors
│  └─ir
├─dogt
├─include
│  ├─dogc
│  │  ├─common
│  │  ├─errors
│  │  └─ir
│  ├─dogt
│  ├─parser
│  └─pcd
├─parser
├─pcd
├─runtime
├─test
│  ├─control
│  ├─output
│  │  ├─control
│  │  └─syntax
│  └─syntax
└─tools

这是结构,tree命令.我的命名规则是,项目根目录每个文件夹存源码(.c/.cpp),include/子目录对应文件夹,比如根目录是dogc实现编译器功能,那么include/下就有dogc目录存储dogc模块的相关.h包含文件.(没用到内联文件哈,项目比较简单)
(个人建议这步操作前存个commit,以免出现意外,并推送到远程服务器)

修改git忽略配置

然后在根目录.下建立VSSolution/目录.在 在VS里操作之前,我们先改掉.gitignore配置,免得版本控制意外加载一些临时文件和不需要的大量文件.
在原有基础上添加如下几行

x86/
x64/
.vs/
*.vcxproj.user

解释下,x86/x64/是屏蔽掉输出的中间文件和最终二进制文件,.vs/是个隐藏文件夹,在VSSolution下面,是vs运行的一些必要数据,不需要存版本库.*.vcxproj.user看这个.user后缀,就能知道是跟本机用户相关的,换台设备就不能使唤了,所以不需要.

创建VS解决方案

然后,在vs里新建项目,名字dog-toolchain(p.s.选择项目类型的时候选空项目就行了,我们自己添加现有文件)
把已有项目转换成Visual Studio的解决方案_第1张图片
注意取消勾选"Place solution and project in the same directory".点击Create.

修正dogt-dll项目的位置

这时文件系统的项目根目录.下有子目录dog-toolchain,手动把里面的文件全都移动到VSSolution/下,这一步请关闭vs,不然文件占用我可不管.
移动后,删掉dog-toolchain目录.因为它失去利用价值.
这时文件系统如下:
把已有项目转换成Visual Studio的解决方案_第2张图片
不要打开.sln,先把这个目录里面的dog-toolchain删掉,待会重建,然后打开.sln:
把已有项目转换成Visual Studio的解决方案_第3张图片
说白了那个"项目"被我们删掉了,无法加载.没关系啊!去掉它重来.
把已有项目转换成Visual Studio的解决方案_第4张图片
remove掉这个项目,点确定
然后add一个新"项目",这里由于我需要编译成dll,所以选DLL项目,(前文说过,实际上是一个组件,对应到toolchain里面就是一个个.exe工具,叫做"项目.vcxproj",整个工具链叫做"解决方案(.sln)")
把已有项目转换成Visual Studio的解决方案_第5张图片

加载进去后,手动删掉里面的几个文件:(选中,摁del,对话框里选"remove"而不是delete,但其实都一样.实际项目中一般选remove,因为delete就把硬盘上也删掉了)
把已有项目转换成Visual Studio的解决方案_第6张图片
然后添加我们的文件~(这是源码,也就是.c文件)
把已有项目转换成Visual Studio的解决方案_第7张图片
正确操作是,选中要添加的文件,(不要选择文件夹,你没法添加的).这里因为项目具体的原因,只选这几个(Dogt.c后面用到,作为"更新exe架构"的例子)
把已有项目转换成Visual Studio的解决方案_第8张图片
添加成功.
把已有项目转换成Visual Studio的解决方案_第9张图片
同理,把include下的相关.h文件添加进去,你会发现不带include/前缀:
把已有项目转换成Visual Studio的解决方案_第10张图片

编码,修正环境变化带来的问题

把已有项目转换成Visual Studio的解决方案_第11张图片
随便打开一个文件,代码量不多,但…报错99+.红条占满右侧!壮观!
仔细一看,多半是名称未定义导致的,那肯定是include出毛病了呗!

添加附加包含目录

在项目dogt-dll上右键(注意不是解决方案,是dogt-dll项目);点最后一个,“属性(Properties)”,沿着路径找到这一步:
把已有项目转换成Visual Studio的解决方案_第12张图片
(依次点击C/C++->Additional Include Directories)
添加相对路径的include path:../../include/
可见错误都没啦~

试着编译却失败

首先,发现有个"您是没添加’pch’文件吗?",那么禁用掉预编译头文件选项不就完了:
项目属性里,一步步点,如下:
把已有项目转换成Visual Studio的解决方案_第13张图片
然后应用,按F5,发现好多警告像下面这样:
把已有项目转换成Visual Studio的解决方案_第14张图片

C4819 The file contains a character that cannot be represented
in the current code page (936).
Save the file in Unicode format to prevent data loss	

翻译过来就是,文件包含一个不能正常时候用的字符在当前代码页下(936),请使用UTF-8保存以免数据丢失(人工翻译的,不一定好听)

解决编码警告

解决请见我的另一篇博客:解决C4819文件编码警告
搞定,警告少多了:(虽然还是很多)
把已有项目转换成Visual Studio的解决方案_第15张图片

指针预判空

可以看见好多警告都是"指针可能为0",那解决方案自然是,在指针初始化之后就判空呗…定义一个宏,便于定位调试信息:

#define CHECK_POINTER(p) if (!(p)) { \
	printf("%s:%d: Pointer is NULL.", __FILE__, __LINE__); \
	abort();}

写在include/debug.h里面,大家都可以引用.
然后在每个需要判空的头文件里加上对debug头的包含,并在源码具体位置调用,就行啦(就像这样)
把已有项目转换成Visual Studio的解决方案_第16张图片
ok,只剩安全性错误(毕竟这源码在gcc下能过.那么说明只会引发环境不同导致的错误而不是代码的错误;这也能说明在CL编译器自身没问题的情况下,我们的程序肯定能跑起来)
把已有项目转换成Visual Studio的解决方案_第17张图片

不安全代码替换

这个错误是说什么呢?sprintf不安全,请用sprintf_s代替.
为什么会这样?sprintf的标准格式是sprintf(char *dst, const char *fmt, ...),想没想过,如果fmt导致的结果字符串太长,假设dst分配了100字节,但凡结果是"100个x+两个y"就有102个字节,算上结尾空字符103,内存:你礼貌吗

sprintf_s则多出来一个参数(在dst前面)size_t count,控制最大字符数,免得超出dst的界限.(当然如果你能认为保证不越界当然很好,但这样确实更安全,你能保证人不出错率跟电脑比?)

把所有sprintf相关的都改一下:
(这里只需要跟malloc的一致就行啦)
把已有项目转换成Visual Studio的解决方案_第18张图片
其实,sprintf_s的第二个参数很麻烦,但是我们可以把fmt的长度.加上格式参数里字符串的长度和,加上预计的%d,%f之类长度,编译器会常量折叠,不必担心效率.

还有fopen和strcpy,同理全部换掉.

当然,如果嫌每次写这么多判空很麻烦,那么定义一个宏,以后直接展开:

// 检测指针非空
#define CHECK_POINTER(p) if (!(p)) { \
	printf("%s:%d: Pointer is NULL.\n", __FILE__, __LINE__); \
	abort();}

// 输出log(编译器bug)
#ifdef __cplusplus
	#define LOG(con) cout << __FILE__ << ":" << __LINE__ << con << endl;
#endif

#define OPENFILE(fs, fn, md) if (fopen_s(&(fs), (fn), (md))) { \
	printf("%s:%d: File open failed.(%s with mode)\n", fn, md); \
	abort(); \
}

引用外部库

由于项目自身原因,我们首先按照同样的方法把parser模块和pcd模块一起迁移过来.这个解析器模块注意,你得手动生成需要的.c和.h,然后添加进项目,这要求语法分析器一次到位(实际上这个项目早就做到了)

这里已经解决完没有任何依赖的libdparserlibpcddll项目(附:写到这里时,dogt的动态库已经被我改成libdogt,所以这段文字以上的截图就不改了)
把已有项目转换成Visual Studio的解决方案_第19张图片
借此机会讲解dll项目的构建哈
我们打开"属性"->“C/C++”->“预处理”->“预处理宏”,会发现它帮我们定义了一个XXX_EXPORTS宏,这个XXX就是项目名去掉特殊字符,全部大写
把已有项目转换成Visual Studio的解决方案_第20张图片
这个宏应该只在dll项目里会被定义,且只在编译时会被定义,也就是说外部程序编译的时候跟这个宏没关系.利用它,我们可以泛华DLL导入导出接口的修饰,控制接口函数到底是导出:暴露给外部使用 还是导入:查找符号并且为当前程序所用
具体代码段如下,在有接口函数的声明的头文件里定理DLLAPI宏:

#ifdef LIBPCD_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif

翻译过来就是,如果定义了导出宏,就把API修饰宏设置成导出,否则导入.
然后在函数的声明(注意实现不需要加)式前加上DLLAPI前缀:
把已有项目转换成Visual Studio的解决方案_第21张图片
注意了,这里建议只添加需要导出的函数,内部处理逻辑就省去了,第一可以免得导入库太大,第二,安全性你懂的.

好了说到正题,libdogt会调用pcd和dparser里的接口,因此我们需要把这两个dll跟libdogt.dll关联起来.
libdogt->References右键,像下面这样,点第一个,“添加引用”
把已有项目转换成Visual Studio的解决方案_第22张图片
把两个要依赖的dll项目勾上,ok
把已有项目转换成Visual Studio的解决方案_第23张图片
这时候"引用"选择器下应该有两项:
把已有项目转换成Visual Studio的解决方案_第24张图片
然后检查下,也就是项目是否正常依赖另一个项目.在libdogt右键,找到一个Build Dependencies,中文应该是"构建依赖"之类的.
会有这个窗口,发现复选框都勾上了,就是对的,这意味着一旦被依赖的项目更改,这个项目会发现并且重构被依赖者,而且这个项目在构建的时候,会先尝试构建被依赖项目,除非它们是最新的.
把已有项目转换成Visual Studio的解决方案_第25张图片
可见,重编译(Rebuild)libdogt,出现如图说明三个项目都重构建了:
把已有项目转换成Visual Studio的解决方案_第26张图片

总结

下面总结下把一个Makefile管理的项目迁移到vs管理模式下的步骤:
1.新建一个子目录,保存vs项目管理文件
2.修改版本控制的忽略选项,让它略过vs自动生成的临时文件
3.配置项目,把路径调整到合适的地方
4.把原有源码添加进.vcxproj使vs能管理
5.根据报错和警告调整代码,让它又安全又高效,满足新的环境所需
6.若有dll,编译时根据EXPORTS符号确认导入导出状态;引用是直接在"引用"里加同一工程下的项目,他会自己找.dll和.lib

续集:把项目切换成release编译以及发布注意事项(DLL项目)

你可能感兴趣的:(学习笔记,visual,studio,c++,c语言,编译器,项目架构)