把已有项目转换成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.选择项目类型的时候选空项目就行了,我们自己添加现有文件)
注意取消勾选"Place solution and project in the same directory".点击Create
.
修正dogt-dll项目的位置
这时文件系统的项目根目录.
下有子目录dog-toolchain
,手动把里面的文件全都移动到VSSolution/
下,这一步请关闭vs,不然文件占用我可不管.
移动后,删掉dog-toolchain
目录.因为它失去利用价值.
这时文件系统如下:
不要打开.sln,先把这个目录里面的dog-toolchain
删掉,待会重建,然后打开.sln:
说白了那个"项目"被我们删掉了,无法加载.没关系啊!去掉它重来.
remove掉这个项目,点确定
然后add一个新"项目",这里由于我需要编译成dll,所以选DLL项目,(前文说过,实际上是一个组件,对应到toolchain里面就是一个个.exe工具,叫做"项目.vcxproj",整个工具链叫做"解决方案(.sln)")
加载进去后,手动删掉里面的几个文件:(选中,摁del,对话框里选"remove"而不是delete,但其实都一样.实际项目中一般选remove,因为delete就把硬盘上也删掉了)
然后添加我们的文件~(这是源码,也就是.c文件)
正确操作是,选中要添加的文件,(不要选择文件夹,你没法添加的).这里因为项目具体的原因,只选这几个(Dogt.c
后面用到,作为"更新exe架构"的例子)
添加成功.
同理,把include下的相关.h文件添加进去,你会发现不带include/前缀:
编码,修正环境变化带来的问题
随便打开一个文件,代码量不多,但…报错99+.红条占满右侧!壮观!
仔细一看,多半是名称未定义导致的,那肯定是include出毛病了呗!
添加附加包含目录
在项目dogt-dll
上右键(注意不是解决方案,是dogt-dll项目);点最后一个,“属性(Properties)”,沿着路径找到这一步:
(依次点击C/C++
->Additional Include Directories
)
添加相对路径的include path:../../include/
可见错误都没啦~
试着编译却失败
首先,发现有个"您是没添加’pch’文件吗?",那么禁用掉预编译头文件选项不就完了:
项目属性里,一步步点,如下:
然后应用,按F5,发现好多警告像下面这样:
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文件编码警告
搞定,警告少多了:(虽然还是很多)
指针预判空
可以看见好多警告都是"指针可能为0",那解决方案自然是,在指针初始化之后就判空呗…定义一个宏,便于定位调试信息:
#define CHECK_POINTER(p) if (!(p)) { \printf("%s:%d: Pointer is NULL.", __FILE__, __LINE__); \abort();}
写在include/debug.h
里面,大家都可以引用.
然后在每个需要判空的头文件里加上对debug头的包含,并在源码具体位置调用,就行啦(就像这样)
ok,只剩安全性错误(毕竟这源码在gcc下能过.那么说明只会引发环境不同导致的错误而不是代码的错误;这也能说明在CL编译器自身没问题的情况下,我们的程序肯定能跑起来)
不安全代码替换
这个错误是说什么呢?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的一致就行啦)
其实,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,然后添加进项目,这要求语法分析器一次到位(实际上这个项目早就做到了)
这里已经解决完没有任何依赖的libdparser
和libpcd
dll项目(附:写到这里时,dogt的动态库已经被我改成libdogt,所以这段文字以上的截图就不改了)
借此机会讲解dll项目的构建哈
我们打开"属性"->“C/C++”->“预处理”->“预处理宏”,会发现它帮我们定义了一个XXX_EXPORTS
宏,这个XXX就是项目名去掉特殊字符,全部大写
这个宏应该只在dll项目里会被定义,且只在编译时会被定义,也就是说外部程序编译的时候跟这个宏没关系.利用它,我们可以泛华DLL导入导出接口的修饰,控制接口函数到底是导出:暴露给外部使用 还是导入:查找符号并且为当前程序所用
具体代码段如下,在有接口函数的声明的头文件里定理DLLAPI宏:
#ifdef LIBPCD_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif
翻译过来就是,如果定义了导出宏,就把API修饰宏设置成导出,否则导入.
然后在函数的声明(注意实现不需要加)式前加上DLLAPI前缀:
注意了,这里建议只添加需要导出的函数,内部处理逻辑就省去了,第一可以免得导入库太大,第二,安全性你懂的.
好了说到正题,libdogt会调用pcd和dparser里的接口,因此我们需要把这两个dll跟libdogt.dll关联起来.
libdogt
->References
右键,像下面这样,点第一个,“添加引用”
把两个要依赖的dll项目勾上,ok
这时候"引用"选择器下应该有两项:
然后检查下,也就是项目是否正常依赖另一个项目.在libdogt
右键,找到一个Build Dependencies
,中文应该是"构建依赖"之类的.
会有这个窗口,发现复选框都勾上了,就是对的,这意味着一旦被依赖的项目更改,这个项目会发现并且重构被依赖者,而且这个项目在构建的时候,会先尝试构建被依赖项目,除非它们是最新的.
可见,重编译(Rebuild)libdogt,出现如图说明三个项目都重构建了:
总结
下面总结下把一个Makefile管理的项目迁移到vs管理模式下的步骤:
1.新建一个子目录,保存vs项目管理文件
2.修改版本控制的忽略选项,让它略过vs自动生成的临时文件
3.配置项目,把路径调整到合适的地方
4.把原有源码添加进.vcxproj使vs能管理
5.根据报错和警告调整代码,让它又安全又高效,满足新的环境所需
6.若有dll,编译时根据EXPORTS符号确认导入导出状态;引用是直接在"引用"里加同一工程下的项目,他会自己找.dll和.lib
续集:把项目切换成release编译以及发布注意事项(DLL项目)
把已有项目转换成Visual Studio的解决方案相关推荐
- 将 Web 项目从 Visual Studio .Net 2002/2003 转换到 Visual Studio 2005 的分步指南
本页内容 转换注意事项 第一部分:准备要转换的 Visual Studio .Net 2002/2003 Web 项目 第二部分:迁移您的 Web 项目 第三部分:完成迁移的迁移后步骤 第四部分:其他 ...
- 将已有项目转为se项目_如何将 Java 项目转换成 Maven 项目
本文内容 Java 项目 Maven 项目 Java 项目转换成 Maven 项目 本文主要介绍如何将 Java 项目转换成 Maven 项目.首先要明确的是,用 Maven 管理 Java 项目的确 ...
- adroid再谈如何将android studio项目转换成eclipse
首先,不要因为编译原因而放弃,studio项目是完全可以转换成eclipse的.先推荐一款好用的网络加速器---小鸟VPN,适合android studio开发者,www.birdsvpn.com. ...
- 为什么转换到Visual Studio 2017如此 “容易”
与VS 2015相比,Visual Studio 2017在C ++功能方面有重大的飞跃.我们希望升级到新版本后能让您的日常工作变得更轻松. 这篇文章主要介绍从Visual Studio 2015升级 ...
- 把Eclipse项目转换成Maven项目
把Maven项目转换成Eclipse项目只要使用Maven的Eclipse插件就能做到 mvn eclipse:eclipse 其实Maven的Eclipse插件也有把Eclipse项目转换成Mave ...
- Vue.JS项目输入框中使用v-model导致number类型值自动转换成string问题的解决方案
老文章了,目前用el-input v-model.number就能解决 很简单的操作,不知道当初在做什么,下文请直接忽略- Vue.JS项目中v-model导致输入框中number类型值自动转换成st ...
- 在 Visual Studio 的解决方案资源管理器中隐藏一些文件
项目文件中有一些属性几乎是专门为 IDE 而准备的,不过考虑到 .NET 生态的开发者多数都使用 Visual Studio,所以基本上也只有 Visual Studio 对这些特性支持的最全面.(才 ...
- c语言此项目已过期是什么意思,Visual Studio 2017 许可证已过期解决方案
JS控制checkbox全选.取消全选.删除功能的代码贴出来.. function checkAll() { var code_Values = document.getElementsByTagNa ...
- C# 处理汉字、拼音、笔画转换 Microsoft Visual Studio International Pack 1.0
(已经安装解压后的dll ,请添加至项目bin下) http://files.cnblogs.com/Fooo/Microsoft_Visual_Studio_International_Pack_1 ...
- vc 将已有项目打包成dll 并应用于其他项目_.NET混淆器 Dotfuscator使用教程:保护你的应用之存档报告文件...
Dotfuscator是一款.NET混淆器和压缩器,防止你的应用程序被反编译. 本篇文章将继续上一篇文章与大家分享保护应用程序的后续三个部分:存档报告文件.加强保护和替代方法. 存档报告文件 作为构建 ...
最新文章
- kubernetes资源对象之security context
- Resnet的pytorch官方实现代码解读
- 【Thymeleaf】获取绝对路径
- 制作类似QQ截图软件
- oracle基本结构
- 桔子浏览器电脑版看不了视频怎么办 视频无法播放怎么解决
- Oracle(三)多行函数
- 今日恐慌与贪婪指数为31 恐慌程度有所上升
- python的最受欢迎的库_年度十大最受欢迎机器学习Python库
- 手机txt拆分器_TXT文本分割器
- Masscan工具使用
- 子查询定义从句总结(WITH AS 语句)
- Python软件编程等级考试四级——20210905
- TLS Lab(Transport Layer Security Lab,SEED实验)基于PKI实验内容进行中间人攻击实验
- 解决使用ssh工具远程连接到服务器上因为网络波动而需要重连的问题
- php7 memcached sasl,启用MemCached的SASL认证
- (PC+WAP)织梦模板茶几茶盘类网站
- 洛谷刷题记录(python)【入门6】函数与结构体
- python超声成像仿真_平面波超声成像(Filed II仿真代码)
- 模式识别-从贝叶斯决策理论看模式分类
热门文章
- 《神奇的数学》读后感_奇妙的数学王国读后感10篇
- shopex服务器信息,ShopEx开放平台
- ARM处理器开发详解
- Spring Framework灰度发布
- 量化交易17-先认识K线形态:处于底部上涨、处于顶部下跌: 弃婴、捉腰带线、脱离、藏婴吞没、反击线、十字孕线、倒锤头、长脚十字、光头光脚/缺影线、上升/下降三法 、跳空并列阴阳线、向上跳空的两只乌鸦
- 奥塔哥大学计算机科学怎样,2019QS世界大学学科排名出炉,新西兰最强专业看过来!...
- qq服务器只保留7天文件吗,qq离线文件服务器上的离线文件能保留几天(一般7天)?...
- android view 前景色,Android开发中的一些小技巧
- 五线谱音名和组别对照表_五线谱上区别各个音的组别
- android 模拟器后缀名,apk是什么文件?apk文件模拟器是什么?