VS2019 - 修正导出项目的工程模板的错误
文章目录
- VS2019 - 修正导出项目的工程模板的错误
- 概述
- 举个例子
- 手工打开模板压缩包看看
- 想查一下,为啥用7zip解开VS2019导出的原始模板工程.zip会出现头错误
- 想自己写个工具来生成VS2019可用的工程模板
- 笔记
- 工程实现
- genVs2019PrjTemplateFile.cpp
- END
VS2019 - 修正导出项目的工程模板的错误
概述
再做实验,写了一些小工程,来验证知识点。
这些小工程是连续的,都是上一个知识点的基础上,进行迭代。
如果开始进一步的新实验,比较简单的方法,是将上一个实验的工程目录拷贝改名为新实验工程。
但是这样的不好的地方是,自己要改.sln, .vcxproj, vcxproj.filters的名称,改的挺麻烦的。
如果只在工程中,改工作区的解决方案和工程的名称,做实验不影响,但是磁盘上的几个工程文件名字没改过来,看着挺膈应。
尝试用VS2019导出工程模板的功能,然后新建自己导出的工程模板,这样挺好的。
但是发现VS2019(社区版)导出模板的功能有bug, 导致从自己导出的模板上新建工程时,工程文件位置不对,工程文件丢失,导致工程无法运行。
举个例子
原始工程布局如下:
D:.
| awesomeface.png
| container.jpg
| fs.cfg
| glad.c
| main.cpp
| my_gl_exp5d1a.sln
| my_gl_exp5d1a.vcxproj
| my_gl_exp5d1a.vcxproj.filters
| my_gl_exp5d1a.vcxproj.user
| vs.cfg
| wall.jpg
|
\---bin_x64_Debugglfw3.dll
将此工程用VS2019的导出模板功能导出后,可以编译过
但是丢了2给原来包含在工程中的配置文件,导致不能运行
从模板上新建的工程,文件布局如下
| awesomeface.png
| container.jpg
| glad.c
| main.cpp
| my_gl_exp5d1a4.sln
| my_gl_exp5d1a4.vcxproj
| my_gl_exp5d1a4.vcxproj.filters
| my_gl_exp5d1a4.vcxproj.user
| wall.jpg
|
\---bin_x64_Debugglfw3.dll
可以看到2个配置文件*.cfg丢了。
手工打开模板压缩包看看
尝试解压,发现zip包头错误,但是文件都能解开。
直觉: VS2019做的zip包是变形的,他们自己可能写入了非标的头标记,用来判断是否为自己的工程模板的格式。
可以VS2019导出的zip包,发现除了*.vstemplate之外,其余文件都和原始的工程是一样的。
直觉: …vstemplate是个配置文件,写着新建工程要落地的文件的列表。
打开…vstemplate看看,果然如此。
看看丢的那2个.cfg在…vstemplate中么?
在,但是位置写错了,莫名奇妙的写错文件路径了。
手工改过来,重新打包,VS2019不认了。无法从改过的工程模板配置新建工程。
最后才搞清楚,原来是工程模板.zip下面,需要直接有.vstemplate才行。相当于工程模板.zip包对应的就是工程的根目录才行。
补齐丢失的2个cfg, 将.vstemplate中的cfg内容和路径都改对,重新打包。
压缩时,名字随便起名,不影响。vs2019是在新建工程向导时,自动解开zip去找…vstemplate, 填在工程向导的条目中。
自己重新打包的VS2019工程模板.zip, 打开后,必须根目录下就有.vstemplate才有效。
在新建工程时,在搜索框中输入自己模板的描述的字符串(e.g. vs2019), 就能找到自己的工程模板,然后新建工程就行。
从自己修正过的模板中新建工程后,编译运行都正常。
但是,如果文件布局稍微复杂一点的工程,不知道VS2019导出模板时,会搞出什么离谱的操作。
如果工程中有几百个文件,手工来修改.vstemplate, 有点不现实,且身为工程师,也不应该手工去改,而是将.vstemplate的格式搞清楚后,写个程序来生成.vstemplate才合理。
想查一下,为啥用7zip解开VS2019导出的原始模板工程.zip会出现头错误
开始直觉是VS2019采用了zip流程,但是改了特定的zip头标记,作为自己判断zip包是否为VS2019工程模板.zip。
当时没多想,去下载了7zip的源码包,在本地编译过(编译出了一个命令行版本的7z.exe).
编译7zip源码为7z.exe的实验,写了笔记(VS2019 - 7zip工程的编译).
VS2019调试命令行为t vs2019_prj_template_1.zip
单步了一下,挺复杂的。
最后跟到报头错误的地方,看到了比较是2个文件名不一样,本来应该是一样的才对。
一个文件是包头文件索引中的文件名,一个是改文件名对应的压缩数据。
这明显就是VS2019的bug了。
单步7z.exe,只能证明是VS2019有bug. 对解决问题(VS2019导出当前工程,作为工程模板)并没有帮助。
但是,让我定位到了,确实"VS2019导出工程模板"的功能有bug, 不是我瞎猜的。仅此而已。
想自己写个工具来生成VS2019可用的工程模板
前面已经想过了,如果纯手工来修复VS2019的工程模板.zip, 如果工程中有100+的文件,受不了。
即使工程中就有3,5个文件,如果需要经常性的做实验工程的模板,也受不了。
既然如此,自己手搓一个工具,将自己想要的工程生成VS2019的工程模板,这样才是正道。
但是,如果按照商业化程序的做法来写这个工具,明显付出和收获不成正比。
在平衡了付出和收获的平衡点之后,采用的方案如下:
- 将选定工程的垃圾都去掉
- 手搓命令行工具,对选定的目录生成VS2019适配的.vstemplate, 且这个.vstemplate就在选定工程的根目录生成。
- 将工程根目录的全部东西都选中(包括.vstemplate),右击,用7zip(或者其他zip工具)打包成.zip(名称随便,只要不和现有的工程模板.zip重名就行,且文件后缀是.zip)
- 将自己做的工程模板.zip丢到VS2019的工程模板目录下
- 新建工程时,输入自己工程模板描述中的关键字(e.g. 2019), 就能找到自己的工程模板,然后就可以从自己的工程模板新建自己的工程了。
- 这种方法,特别适合于做一个连续迭代的实验.
笔记
花了2天,手搓了一个工具.
编程环境 : vs2019 c++ console
效果如下:
D:\my_dev\my_tools\vs2019_prj_template\case>genVs2019PrjTemplateFile.exe D:\my_dev\my_tools\vs2019_prj_template\case\my_gl_exp5d1a
genVs2019PrjTemplateFile 0.1
argc = 2
argv[0] = genVs2019PrjTemplateFile.exe
argv[1] = D:\my_dev\my_tools\vs2019_prj_template\case\my_gl_exp5d1a
task begin
found file : awesomeface.png
found file : bin_x64_Debug\glfw3.dll
found file : container.jpg
found file : fs.cfg
found file : glad.c
found file : main.cpp
found file : my_gl_exp5d1a.vcxproj
found file : my_gl_exp5d1a.vcxproj.filters
found file : vs.cfg
found file : wall.jpg
task end
工程文件为 - my_gl_exp5d1a.vcxproj
工程名称为 - my_gl_exp5d1a
工程过滤文件为 - my_gl_exp5d1a.vcxproj.filters
工程模板中包含的其他文件数量为8个
[成功] 保存VS2019工程模板配置文件 D:\my_dev\my_tools\vs2019_prj_template\case\my_gl_exp5d1a\my_gl_exp5d1a.vstemplate
[成功]
请全选工程根目录下的所有文件
右击 >> 压缩 >> zip文件
该.zip文件为VS2019工程模板
将此.zip放到VS2019的工程模板目录中(e.g. C:\Users\usr_name\Documents\Visual Studio 2019\Templates\ProjectTemplates\)
用VS2019新建工程, 选择"所有语言/所有平台/所有项目类型", 搜索自己模板描述的关键字(e.g. vs2019), 就可以找到自己的工程模板
然后就可以用自己的工程模板新建工程了, enjoy :-P
process files(and dirs) cnt = 10处理成功ENDD:\my_dev\my_tools\vs2019_prj_template\case>
工程实现
工程除了用到tinyxml2.6.2, 其他实现都在一个genVs2019PrjTemplateFile.cpp中
genVs2019PrjTemplateFile.cpp
500行的实现,花了2天
// @file genVs2019PrjTemplateFile.cpp
// @brief 在给定VS2019工程文件夹中创建vs2019工程模板文件 t.vstemplate
// @usage THE_EXE vs2019_prj_dir
// @note 程序需要的参数, 全部从给定的工程文件夹中提取#include <iostream>
#include <sstream>#include <io.h>
#include <string>
#include <vector>// #include "tinyxml.h"
#include "tinyxml/tinyxml.h"#define PROG_VER "0.1"struct TAG_PARAM; // 前向声明bool callback_my_fn(TAG_PARAM& param);
void do_task_begin(TAG_PARAM& param);
void do_task_ing(TAG_PARAM& param);
void do_task_end(TAG_PARAM& param);
typedef bool (*PFN_CALLBACK_MY_FN)(TAG_PARAM& param);struct TAG_PARAM {std::string str_gen_vs2019_prj_template_cfg_file_path_name;std::string str_prj_name; // 工程名称std::string str_prj_file_name; // 工程文件名称 x.vcxprojstd::string str_prj_filters_file_name;// 工程过滤文件名称 x.vcxproj.filtersstd::vector<std::string> vec_files_was_found; // 最终找到的有效文件int iCntTotal; // 遍历后,进入回调的文件数量bool is_task_begin; // 任务开始bool is_task_end; // 任务结束bool is_file_or_dir; // true = file, false = dirstd::string strCurFileOrDir;std::string strRelativePath; // 相对文件路径,用于写入vs2019模板配置文件// 工程根目录// e.g. x:\\dir_topstd::string str_prj_root_dir;// 工程根目录下的垃圾目录// .vs// debug// my_res\\temp_dir1std::vector<std::string> vec_str_trush_dirs;// 工程根目录下的垃圾文件// temp1.txt// temp_dir\\temp2.txtstd::vector<std::string> vec_str_trush_files;PFN_CALLBACK_MY_FN pfn_cb;
};void show_cmdline_param(int argc, char** argv);
bool is_ok_cmdline_param(int argc, char** argv);
void usage();
bool process_dir(char* pszPath);
bool find_all_files_in_dir(char* pszPath, TAG_PARAM& param);
bool is_obj_exist(std::string& str_root_path, const std::vector<std::string> vec, std::string str_to_find);
std::string sanitize_path_separator(std::string str_path); // 规范路径末尾的分隔符
std::string remove_root_path(std::string str_full_path, std::string str_root_path);
bool reverse_find(std::string str, std::string str_to_find);
bool find_postfix_and_delete(std::vector<std::string>& vec, std::string strPostfix, std::string& strWasFound);
bool get_file_pre_and_postfix(std::string strFilename, std::string& strPrefix, std::string& strPostfix);
std::string get_FileName_from_FilePathName(std::string str_file_path_name);
bool gen_vs2019_prj_template_cfg_file(TAG_PARAM& param);int main(int argc, char** argv) {bool b_rc = false;do {std::cout << "genVs2019PrjTemplateFile " << PROG_VER << std::endl;show_cmdline_param(argc, argv);if (!is_ok_cmdline_param(argc, argv)) {usage();break;}b_rc = process_dir(argv[1]);} while (false);std::cout << "\r\n\r\n";std::cout << "处理" << (b_rc ? "成功" : "失败") << std::endl;std::cout << "\r\n\r\n";std::cout << "END" << std::endl;return EXIT_SUCCESS;
}void show_cmdline_param(int argc, char** argv) {std::cout << "argc = " << argc << std::endl;for (int i = 0; i < argc; i++) {std::cout << "argv[" << i << "] = " << argv[i] << std::endl;}
}bool is_ok_cmdline_param(int argc, char** argv) {bool b_rc = false;do {if (argc < 2) {break;}b_rc = true;} while (false);return b_rc;
}void usage() {std::cout << "\r\n\r\n";std::cout << "USEAGE:" << std::endl;std::cout << "THE_EXE a_vs2019_project_dir_full_path_name" << std::endl;std::cout << "备注:" << std::endl;std::cout << "\t做工程模板配置文件之前, 先将不属于工程的文件/临时文件删干净" << std::endl;
}bool process_dir(char* pszPath) {bool b_rc = false;TAG_PARAM param;do {param.str_prj_root_dir = pszPath;param.vec_str_trush_dirs.push_back(".vs");param.vec_str_trush_files.push_back("vcxproj.user");param.vec_str_trush_files.push_back(".sln");param.is_task_begin = true;param.is_task_end = false;param.pfn_cb = callback_my_fn;if (!param.pfn_cb(param)) {break;}param.is_task_begin = false;param.is_task_end = false;if (!find_all_files_in_dir(pszPath, param)) {break;}param.is_task_begin = false;param.is_task_end = true;param.pfn_cb = callback_my_fn;if (!param.pfn_cb(param)) {break;}b_rc = true;} while (false);std::cout << "process files(and dirs) cnt = " << param.iCntTotal << std::endl;return b_rc;
}// @fn bool find_all_files_in_dir(char* pszPath)
// @param char* pszPath, 要操作的目录名称的全路径
bool find_all_files_in_dir(char* pszPath, TAG_PARAM& param) {struct _finddata_t fileinfo;intptr_t hFile = _findfirst((std::string(pszPath) + "\\*.*").c_str(), &fileinfo);if (hFile == -1) {std::cout << "find file failed - " << pszPath << std::endl;return false;}do {// 排除当前目录和父目录if (strcmp(fileinfo.name, ".") == 0 ||strcmp(fileinfo.name, "..") == 0) {continue;}std::string fullPath = std::string(pszPath) + "\\" + fileinfo.name;param.strCurFileOrDir = fullPath;if (fileinfo.attrib & _A_SUBDIR) { // 子目录递归遍历param.is_file_or_dir = false;if (param.pfn_cb(param)) {find_all_files_in_dir(const_cast<char*>(fullPath.c_str()), param);}} else { // 文件处理param.is_file_or_dir = true;param.pfn_cb(param);}} while (_findnext(hFile, &fileinfo) == 0);_findclose(hFile);return true;
}bool callback_my_fn(TAG_PARAM& param) {bool b_rc = false;std::string strRelativePath; // 相对文件路径do {if (param.is_task_begin) {do_task_begin(param);} else if (param.is_task_end) {do_task_end(param);} else {std::string str_to_find = param.strCurFileOrDir;if (param.is_file_or_dir) {// is fileif (!is_obj_exist(param.str_prj_root_dir, param.vec_str_trush_files, str_to_find)) {// 不是垃圾文件// std::cout << "file : " << param.strCurFileOrDir << std::endl;// 从str_to_find中去掉param.str_prj_root_dir, 得到不带路径的相对文件路径(e.g. dir1\\b.txt)strRelativePath = remove_root_path(str_to_find, param.str_prj_root_dir);if (strRelativePath.empty()) {__debugbreak();}// 将 strRelativePath 写入VS2019的工程模板配置param.strRelativePath = strRelativePath;do_task_ing(param);} else {// 是垃圾, 外面不要再遍历// 对于文件无所谓, 外面不判断回调是否返回falsebreak;}} else if (!param.is_file_or_dir) {// is dirif (!is_obj_exist(param.str_prj_root_dir, param.vec_str_trush_dirs, str_to_find)) {// 不是垃圾文件// param.iCntTotal++; // 如果是纯目录, 不用做写入VS2019模板配置// 目录不用写到VS2019模板配置中,只用于调试// std::cout << "dir : " << param.strCurFileOrDir << std::endl;} else {// 是垃圾, 外面不要再遍历// 外面有判断,如果文件夹遍历时,回调返回false, 将不会再遍历此文件夹break;}}}b_rc = true; // 需要继续处理} while (false);return b_rc;
}bool reverse_find(std::string str, std::string str_to_find) {return str.rfind(str_to_find) != std::string::npos;
}// 查找str_to_find是否在vec中,请实现
bool is_obj_exist(std::string& str_root_path, const std::vector<std::string> vec, std::string str_to_find) {std::vector<std::string>::const_iterator it;std::string str_full_path;bool b_find = false;char c = '\0';for (it = vec.begin(); it != vec.end(); it++) {str_full_path = sanitize_path_separator(str_root_path);str_full_path += *it;if (std::string::npos != str_to_find.find(str_full_path)) {// 找到了垃圾b_find = true;break;} else {// 找 ".sln" 这样的,从后往前找if (reverse_find(str_to_find, *it)) {b_find = true;break;}}}return b_find;
}std::string sanitize_path_separator(std::string str_path) {std::string str_full_path = str_path;char c = str_full_path.back();if (('/' != c) && ('\\' != c)) {str_full_path += '\\';}return str_full_path;
}std::string remove_root_path(std::string str_full_path, std::string str_root_path) {// 检查完整路径是否以根路径开头if (str_full_path.find(str_root_path) == 0) {// 获取根路径的长度size_t root_length = str_root_path.length();// 检查根路径后是否有路径分隔符if (root_length < str_full_path.length() && (str_full_path[root_length] == '\\')) {// 跳过路径分隔符root_length++;}// 返回从根路径之后开始的子字符串return str_full_path.substr(root_length);}// 如果完整路径不以根路径开头,返回空return "";
}void do_task_begin(TAG_PARAM& param) {std::cout << "task begin" << std::endl;param.iCntTotal = 0;param.vec_files_was_found.clear();
}void do_task_ing(TAG_PARAM& param) {std::cout << "found file : " << param.strRelativePath << std::endl;param.vec_files_was_found.push_back(param.strRelativePath);param.iCntTotal++;
}void do_task_end(TAG_PARAM& param) {std::cout << "task end" << std::endl;std::string str_postfix;do {// 从param.vec_files_was_found中找到*.vcxproj, 并删除 *。vcprojif (!find_postfix_and_delete(param.vec_files_was_found, ".vcxproj", param.str_prj_file_name)) {std::cout << "error - 没有找到工程文件 *.vcxproj" << std::endl;break;}std::cout << "工程文件为 - " << param.str_prj_file_name << std::endl;if (!get_file_pre_and_postfix(param.str_prj_file_name, param.str_prj_name, str_postfix)) {std::cout << "error 没有找到工程名称 - " << param.str_prj_file_name << std::endl;break;}std::cout << "工程名称为 - " << param.str_prj_name << std::endl;param.str_prj_filters_file_name.clear();if (!find_postfix_and_delete(param.vec_files_was_found, ".vcxproj.filters", param.str_prj_filters_file_name)) {std::cout << "warning - 没有找到工程过滤文件 .vcxproj.filters" << std::endl;}std::cout << "工程过滤文件为 - " << param.str_prj_filters_file_name << std::endl;std::cout << "工程模板中包含的其他文件数量为" << param.vec_files_was_found.size() << "个" << std::endl;if (!gen_vs2019_prj_template_cfg_file(param)) {break;}std::cout << "[成功]\r\n"<< "请全选工程根目录下的所有文件\r\n"<< "右击 >> 压缩 >> zip文件\r\n"<< "该.zip文件为VS2019工程模板\r\n"<< "将此.zip放到VS2019的工程模板目录中(e.g. C:\\Users\\usr_name\\Documents\\Visual Studio 2019\\Templates\\ProjectTemplates\\)\r\n"<< "用VS2019新建工程, 选择\"所有语言/所有平台/所有项目类型\", 搜索自己模板描述的关键字(e.g. vs2019), 就可以找到自己的工程模板\r\n"<< "然后就可以用自己的工程模板新建工程了, enjoy :-P"<< std::endl;} while (false);
}bool gen_vs2019_prj_template_cfg_file(TAG_PARAM& param) {bool b_rc = false;std::string strTemp;std::stringstream ss;std::vector<std::string>::const_iterator it;do {param.str_gen_vs2019_prj_template_cfg_file_path_name = sanitize_path_separator(param.str_prj_root_dir);param.str_gen_vs2019_prj_template_cfg_file_path_name += param.str_prj_name;param.str_gen_vs2019_prj_template_cfg_file_path_name += ".vstemplate";// 创建 XML 文档对象TiXmlDocument doc;// <VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">TiXmlElement* root = new TiXmlElement("VSTemplate");root->SetAttribute("Version", "3.0.0");root->SetAttribute("xmlns", "http://schemas.microsoft.com/developer/vstemplate/2005");root->SetAttribute("Type", "Project");doc.LinkEndChild(root);// <TemplateData>TiXmlElement* templateData = new TiXmlElement("TemplateData");root->LinkEndChild(templateData);// <Name>ConsoleApplication1</Name>TiXmlElement* name = new TiXmlElement("Name");TiXmlText* nameText = new TiXmlText(param.str_prj_name.data());name->LinkEndChild(nameText);templateData->LinkEndChild(name);// <Description>my prj template</Description>TiXmlElement* description = new TiXmlElement("Description");ss << "my vs2019 prj template - " << param.str_prj_name;strTemp = ss.str();ss.str("");ss.clear();TiXmlText* descriptionText = new TiXmlText(strTemp.data());description->LinkEndChild(descriptionText);templateData->LinkEndChild(description);// <ProjectType>VC</ProjectType>TiXmlElement* projectType = new TiXmlElement("ProjectType");TiXmlText* projectTypeText = new TiXmlText("VC");projectType->LinkEndChild(projectTypeText);templateData->LinkEndChild(projectType);// <ProjectSubType></ProjectSubType>TiXmlElement* projectSubType = new TiXmlElement("ProjectSubType");templateData->LinkEndChild(projectSubType);// <SortOrder>1000</SortOrder>TiXmlElement* sortOrder = new TiXmlElement("SortOrder");TiXmlText* sortOrderText = new TiXmlText("1000");sortOrder->LinkEndChild(sortOrderText);templateData->LinkEndChild(sortOrder);// <CreateNewFolder>true</CreateNewFolder>TiXmlElement* createNewFolder = new TiXmlElement("CreateNewFolder");TiXmlText* createNewFolderText = new TiXmlText("true");createNewFolder->LinkEndChild(createNewFolderText);templateData->LinkEndChild(createNewFolder);// <DefaultName>ConsoleApplication1</DefaultName>TiXmlElement* defaultName = new TiXmlElement("DefaultName");TiXmlText* defaultNameText = new TiXmlText(param.str_prj_name.data());defaultName->LinkEndChild(defaultNameText);templateData->LinkEndChild(defaultName);// <ProvideDefaultName>true</ProvideDefaultName>TiXmlElement* provideDefaultName = new TiXmlElement("ProvideDefaultName");TiXmlText* provideDefaultNameText = new TiXmlText("true");provideDefaultName->LinkEndChild(provideDefaultNameText);templateData->LinkEndChild(provideDefaultName);// <LocationField>Enabled</LocationField>TiXmlElement* locationField = new TiXmlElement("LocationField");TiXmlText* locationFieldText = new TiXmlText("Enabled");locationField->LinkEndChild(locationFieldText);templateData->LinkEndChild(locationField);// <EnableLocationBrowseButton>true</EnableLocationBrowseButton>TiXmlElement* enableLocationBrowseButton = new TiXmlElement("EnableLocationBrowseButton");TiXmlText* enableLocationBrowseButtonText = new TiXmlText("true");enableLocationBrowseButton->LinkEndChild(enableLocationBrowseButtonText);templateData->LinkEndChild(enableLocationBrowseButton);// <Icon>__TemplateIcon.ico</Icon>TiXmlElement* icon = new TiXmlElement("Icon");// TiXmlText* iconText = new TiXmlText("__TemplateIcon.ico");// icon->LinkEndChild(iconText);templateData->LinkEndChild(icon);// <TemplateContent>TiXmlElement* templateContent = new TiXmlElement("TemplateContent");root->LinkEndChild(templateContent);// <Project TargetFileName="ConsoleApplication1.vcxproj" File="ConsoleApplication1.vcxproj" ReplaceParameters="true">TiXmlElement* project = new TiXmlElement("Project");project->SetAttribute("TargetFileName", param.str_prj_file_name.data());project->SetAttribute("File", param.str_prj_file_name.data());project->SetAttribute("ReplaceParameters", "true");templateContent->LinkEndChild(project);// 添加工程过滤文件// <ProjectItem ReplaceParameters="false" TargetFileName="$projectname$.vcxproj.filters">ConsoleApplication1.vcxproj.filters</ProjectItem>if (!param.str_prj_filters_file_name.empty()) {TiXmlElement* projectItem1 = new TiXmlElement("ProjectItem");projectItem1->SetAttribute("ReplaceParameters", "false");projectItem1->SetAttribute("TargetFileName", "$projectname$.vcxproj.filters");TiXmlText* projectItem1Text = new TiXmlText(param.str_prj_filters_file_name.data());projectItem1->LinkEndChild(projectItem1Text);project->LinkEndChild(projectItem1);}// 循环添加 param.vec_files_was_found 中的工程目录中的其他文件for (it = param.vec_files_was_found.begin(); it != param.vec_files_was_found.end(); it++) {// <ProjectItem ReplaceParameters="false" TargetFileName="ConsoleApplication1.cpp">ConsoleApplication1.cpp</ProjectItem>// <ProjectItem ReplaceParameters="false" TargetFileName="glfw3.dll">BIN_X64_DEBUG\glfw3.dll</ProjectItem>TiXmlElement* projectItem1 = new TiXmlElement("ProjectItem");projectItem1->SetAttribute("ReplaceParameters", "false");strTemp = *it;// TargetFileName的值必须是不带路径的文件名(e.g. "test.dat"), 否则无法出现在VS2019工程中projectItem1->SetAttribute("TargetFileName", get_FileName_from_FilePathName(strTemp).data());// ProjectItem的值是相对路径名称(e.g. BIN_X64_DEBUG\glfw3.dll)TiXmlText* projectItem1Text = new TiXmlText(strTemp.data());projectItem1->LinkEndChild(projectItem1Text);project->LinkEndChild(projectItem1);}// 保存VS2019工程模板配置文件到工程的根目录b_rc = doc.SaveFile(param.str_gen_vs2019_prj_template_cfg_file_path_name.data());std::cout << "[" << (b_rc ? "成功" : "失败") << "] 保存VS2019工程模板配置文件 " <<param.str_gen_vs2019_prj_template_cfg_file_path_name << std::endl;} while (false);return b_rc;
}bool get_file_pre_and_postfix(std::string strFilename, std::string& strPrefix, std::string& strPostfix) {// 从文件名strFilename中取得文件前缀名称strPrefix和文件后缀名称strPostfix// 测试用例// strFilename = "test.dat";// 分隔符号为 '.'// strPrefix = "test";// strPostfix = "dat";// 如果有分隔符号'.', 并取得了前后缀, 返回true; 否则返回false// 查找分隔符号 '.' 的位置size_t dotPos = strFilename.find('.');// 如果找到了分隔符号 '.'if (dotPos != std::string::npos) {// 提取文件前缀strPrefix = strFilename.substr(0, dotPos);// 提取文件后缀strPostfix = strFilename.substr(dotPos + 1);return true;}return false;
}bool find_postfix_and_delete(std::vector<std::string>& vec, std::string strPostfix, std::string& strWasFound) {// 从 vec 中找到 后缀为 strPostfix的文件,并赋值到 strWasFound, 并返回true// 如果没有找到,返回false// 测试用例 :// vec中有一个项为 "data\\a.proj"// strPostfix 为 ".proj"// 函数返回true, strWasFound = "data\\a.proj";for (auto it = vec.begin(); it != vec.end(); ++it) {const std::string& current = *it;if (current.length() >= strPostfix.length() &¤t.compare(current.length() - strPostfix.length(), strPostfix.length(), strPostfix) == 0) {strWasFound = current;vec.erase(it);return true;}}return false;
}std::string get_FileName_from_FilePathName(std::string str_file_path_name) {// 从'全路径文件名称'或者'文件相对路径名称'中, 得到不带路径的文件名称// 测试用例// str_file_path_name = "d:\\my_temp\\test.dat";// str_file_path_name = "my_temp\\test.dat"// str_file_path_name = ".\test.dat"// 以上3这种测试用例, 使本函数都返回 "test.dat"// 查找最后一个路径分隔符的位置,Windows 下是反斜杠 '\',为了在字符串中表示需要转义为 '\\',同时考虑 Unix 下的斜杠 '/'size_t pos = str_file_path_name.find_last_of("\\/");if (pos != std::string::npos) {// 如果找到了路径分隔符,返回分隔符之后的部分,即文件名return str_file_path_name.substr(pos + 1);}// 如果没有找到路径分隔符,说明输入的本身就是文件名return str_file_path_name;
}