first commit

This commit is contained in:
taynpg 2024-03-08 12:00:42 +08:00
commit c41c808834
16 changed files with 5970 additions and 0 deletions

12
.clang-format Normal file
View File

@ -0,0 +1,12 @@
# .clang-format
# 风格格式化
BasedOnStyle: Google
# 4 空格缩进
IndentWidth: 4
# 连续对齐变量的声明
AlignConsecutiveDeclarations: true
# 指针左侧对齐
PointerAlignment: Left
# 访问说明符(public、private等)的偏移
AccessModifierOffset: -4

7
.clangd Normal file
View File

@ -0,0 +1,7 @@
Hover:
ShowAKA: Yes
Diagnostics:
UnusedIncludes: None
Suppress: [anon_type_definition]
ClangTidy:
Remove: misc-unused-alias-decls

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
build
cmake-build*
.idea
.vs
.cache

34
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,34 @@
{
"files.autoSave": "onFocusChange",
"editor.fontSize": 17,
"editor.fontFamily": "'Operator Mono Lig Light', 'Operator Mono Lig Light', 'Operator Mono Lig Light'",
"editor.fontLigatures": true,
"cmake.configureOnOpen": true,
"cmake.generator": "Ninja",
"cmake.debugConfig": {
"console": "integratedTerminal",
"args": [
"G:\\ACode\\RTM_x64_vs2015\\RTM_x64_vs2015_Use.sln",
"Debug|x64",
"D:/he.json",
"expect:..\\include"
],
},
"cmake.options.statusBarVisibility": "visible",
"editor.inlayHints.enabled": "off",
"C_Cpp.intelliSenseEngine": "disabled",
"clangd.arguments": [
"--header-insertion=never",
"--all-scopes-completion",
"--completion-style=detailed",
"-j=4",
"--pch-storage=memory",
"--compile-commands-dir=build",
"--background-index",
"--ranking-model=heuristics",
"--query-driver=/usr/bin/g++"
],
"files.associations": {
"filesystem": "cpp"
}
}

2
3rd/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
jsoncpp-lib
jsoncpp-1.9.5

1
3rd/build_command.txt Normal file
View File

@ -0,0 +1 @@
cmake .. -A x64 -DCMAKE_INSTALL_PREFIX=G:\0516\vs_generate\3rd\jsoncpp-lib -DJSONCPP_WITH_CMAKE_PACKAGE=ON

BIN
3rd/jsoncpp-1.9.5.zip Normal file

Binary file not shown.

27
CMakeLists.txt Normal file
View File

@ -0,0 +1,27 @@
cmake_minimum_required (VERSION 3.8)
cmake_policy(SET CMP0074 NEW)
project (vs_generate)
set(CMAKE_CXX_STANDARD 17)
set(PROJECT_SOURCE
main.cpp vs_generate.cpp
vs_generate.h tinyxml2.h tinyxml2.cpp
vs_json.cpp vs_json.h
)
set(JSONCPP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rd/jsoncpp-lib)
if (MSVC)
string(APPEND CMAKE_CXX_FLAGS " /source-charset:utf-8 /EHsc")
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
include_directories(${JSONCPP_DIR}/include)
link_directories(${JSONCPP_DIR}/lib)
add_executable(vs_generate ${PROJECT_SOURCE})
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
target_link_libraries(vs_generate PRIVATE jsoncpp_staticD)
else()
target_link_libraries(vs_generate PRIVATE jsoncpp_static)
endif()

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# 说明
根据 vcxproj 或者 sln 工程文件导出 clangd 使用的 compile_commands.json 文件。

37
main.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "vs_json.h"
#define VS_GENERATE_VERSION "v1.0"
int main(int argc, char** argv) {
//
if (argc != 5) {
std::string help(VS_GENERATE_VERSION);
help.append("\n");
help.append(
"vs_generate [xx.sln或xxx.vcxproj] [Debug|x64] [xxxx.json] "
"[expect:xxx;ccc;]");
help.append("\n expect选项没有则:后空着即可。");
std::cout << help << std::endl;
return 0;
}
std::string sln_path(argv[1]);
VsParseSln sln;
if (!sln.parse(sln_path)) {
return -1;
}
VsParsePro pro{};
auto projs = sln.get_project();
for (auto& item : projs) {
if (!pro.parse(&item, std::string(argv[2]))) {
continue;
}
}
JsonCommand command{};
command.set_expect(std::string(argv[4]));
command.set_out(std::string(argv[3]));
command.generate(projs);
return 0;
}

2986
tinyxml2.cpp Normal file

File diff suppressed because it is too large Load Diff

2380
tinyxml2.h Normal file

File diff suppressed because it is too large Load Diff

200
vs_generate.cpp Normal file
View File

@ -0,0 +1,200 @@
#include "vs_generate.h"
#include <cassert>
#include <fstream>
#include <regex>
#include <string>
VsParseSln::VsParseSln() = default;
// 解析关联
void VsParseSln::parse_relate() {
for (auto& proj : projs_) {
for (auto& relate : proj.guid_relate_) {
std::string opth = hashmap_[relate];
proj.relate_.push_back(opth);
}
}
}
bool VsParseSln::parse(const std::string& path) {
std::ifstream sln_file(path, std::ios::in);
if (!sln_file.is_open()) {
std::cout << "没有找到 sln 文件或者打开文件失败,路径:" << path
<< "\n";
return false;
}
fs::path full_path(path);
fs::path parent_path = full_path.parent_path();
projs_.clear();
std::string tmp{};
std::regex re(R"(\"(.*?)\")");
std::regex re2(R"((\{.*?\}))");
std::smatch match{};
ProjectInfo proj{};
bool related = false;
bool add = false;
while (std::getline(sln_file, tmp)) {
if (tmp.find("Project") != std::string::npos &&
tmp.find(".vcxproj") != std::string::npos) {
std::vector<std::string> vec{};
while (std::regex_search(tmp, match, re)) {
vec.push_back(match[1]);
tmp = match.suffix().str();
}
// 这里应该有4个结果
assert(vec.size() == 4);
proj.guid_ = vec[3];
proj.path_ = parent_path.string() + "\\" + vec[2];
add = true;
}
if (tmp.find("ProjectDependencies") != std::string::npos) {
related = true;
continue;
}
if (tmp.find("EndProjectSection") != std::string::npos) {
related = false;
continue;
}
if (related) {
// 这里要简单处理一下
if (std::regex_search(tmp, match, re2)) {
std::string restr = match[1];
proj.guid_relate_.push_back(restr);
}
}
if (tmp.find("EndProject") != std::string::npos && add) {
projs_.push_back(proj);
hashmap_[proj.guid_] = proj.path_;
proj.guid_relate_.clear();
add = false;
}
}
parse_relate();
sln_file.close();
return true;
}
std::vector<ProjectInfo> VsParseSln::get_project() const { return projs_; }
void VsParsePro::handle_include(tinyxml2::XMLElement* node,
const std::string& key) const {
if (!node) {
return;
}
const char* data = node->Attribute("Condition");
if (!data) {
return;
}
if (std::string(data).find(key) == std::string::npos) {
return;
}
tinyxml2::XMLElement* purpose = node->FirstChildElement();
while (purpose) {
const char* name = purpose->Name();
if (std::strcmp(name, "IncludePath") != 0) {
purpose = purpose->NextSiblingElement();
continue;
}
const char* include_path = purpose->GetText();
current_proj_->addtion_.append(";");
current_proj_->addtion_.append(include_path);
break;
}
}
void VsParsePro::additon_include_and_predefine(tinyxml2::XMLElement* node,
const std::string& key) const {
if (!node) {
return;
}
const char* data = node->Attribute("Condition");
if (!data) {
return;
}
if (std::string(data).find(key) == std::string::npos) {
return;
}
tinyxml2::XMLElement* cl = get_child_element(node, "ClCompile");
const tinyxml2::XMLElement* purpose =
get_child_element(cl, "AdditionalIncludeDirectories");
if (purpose) {
const char* addtion_dir = purpose->GetText();
current_proj_->addtion_.append(";");
current_proj_->addtion_.append(addtion_dir);
}
purpose = get_child_element(cl, "PreprocessorDefinitions");
if (purpose) {
const char* predefinition = purpose->GetText();
current_proj_->predefinition_.append(";");
current_proj_->predefinition_.append(predefinition);
}
}
tinyxml2::XMLElement* VsParsePro::get_child_element(tinyxml2::XMLElement* node,
const std::string& key) {
tinyxml2::XMLElement* result{};
if (!node) {
return result;
}
tinyxml2::XMLElement* n = node->FirstChildElement();
while (n) {
if (std::strcmp(n->Name(), key.c_str()) == 0) {
result = n;
break;
}
n = n->NextSiblingElement();
}
return result;
}
bool VsParsePro::parse(ProjectInfo* proj, const std::string& key) {
current_proj_ = proj;
supplement(current_proj_);
int ret = m_doc_.LoadFile(current_proj_->path_.c_str());
if (ret != 0) {
std::cout << "解析失败:" << current_proj_->path_ << "\n";
return false;
}
tinyxml2::XMLElement* element = m_doc_.FirstChildElement();
if (std::strcmp(element->Name(), "Project") != 0) {
std::cout << "不是合法的vcxproj文件。" << current_proj_->path_ << "\n";
return false;
}
addition_dir_.clear();
tinyxml2::XMLElement* node = element->FirstChildElement();
while (node) {
if (std::strcmp(node->Name(), "PropertyGroup") == 0) {
handle_include(node, key);
}
if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) {
additon_include_and_predefine(node, key);
}
node = node->NextSiblingElement();
}
tidy(current_proj_);
return true;
}
// 对初步解析出的工程进行整理
void VsParsePro::tidy(ProjectInfo* proj) {}
// 补充其他信息
void VsParsePro::supplement(ProjectInfo* proj) {
if (proj->path_.empty() || !fs::exists(proj->path_)) {
return;
}
fs::path path(proj->path_);
if (proj->proj_root_.empty()) {
proj->proj_root_ = path.parent_path().string();
}
if (proj->name_.empty()) {
proj->name_ = path.filename().string();
}
}

83
vs_generate.h Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include "tinyxml2.h"
#if defined(_MSC_VER) && !defined(__MINGW__)
#define CPP_STANDARD _MSVC_LANG
#else
#define CPP_STANDARD __cplusplus
#endif
#if CPP_STANDARD >= 201703L // 检查C++17或更高版本支持
#include <filesystem>
namespace fs = std::filesystem;
#elif CPP_STANDARD >= 201402L // 检查C++14或更高版本支持
#include <filesystem>
namespace fs = std::experimental::filesystem;
#else
#error "不支持的C++标准版本"
#endif
struct ProjectInfo {
std::string proj_root_{};
std::string path_{};
std::string name_{};
std::string guid_{};
// addtion_ ; 分隔
std::string addtion_{};
// predefinitaion ; 分隔
std::string predefinition_{};
// 关联的工程名称
std::vector<std::string> relate_{};
// 关联的GUID
std::vector<std::string> guid_relate_{};
};
class VsParseSln {
public:
VsParseSln();
~VsParseSln() = default;
public:
bool parse(const std::string& path);
std::vector<ProjectInfo> get_project() const;
private:
// 解析关联
void parse_relate();
private:
std::vector<ProjectInfo> projs_{};
std::unordered_map<std::string, std::string> hashmap_{};
};
class VsParsePro {
private:
tinyxml2::XMLDocument m_doc_{};
std::string addition_dir_{};
ProjectInfo* current_proj_{};
public:
VsParsePro() = default;
~VsParsePro() = default;
private:
void handle_include(tinyxml2::XMLElement* node, const std::string& key) const;
void additon_include_and_predefine(tinyxml2::XMLElement* node,
const std::string& key) const;
static tinyxml2::XMLElement* get_child_element(tinyxml2::XMLElement* node,
const std::string& key);
// 对初步解析出的工程进行整理
void tidy(ProjectInfo* proj);
// 补充其他信息
static void supplement(ProjectInfo* proj);
public:
// 对给定的工程初步解析
bool parse(ProjectInfo* proj, const std::string& key);
};

167
vs_json.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "vs_json.h"
#include <fstream>
#include <list>
#include <set>
JsonCommand::JsonCommand() = default;
JsonCommand::~JsonCommand() = default;
void JsonCommand::set_expect(const std::string& exp) { expect_ = exp; }
void JsonCommand::set_out(const std::string& out) { out_ = out; }
bool JsonCommand::generate(const std::vector<ProjectInfo>& projs) {
Json::Value json_root{};
for (const auto& item : projs) {
const auto& cpps = get_cpp_paths(item.proj_root_);
std::string command = get_command(&item, projs);
for (const auto& cpp : cpps) {
Json::Value jsn{};
jsn["directory"] = item.proj_root_;
jsn["command"] = command + " -c " + cpp; // NOLINT
jsn["file"] = cpp;
json_root.append(jsn);
}
}
std::ofstream jsf(out_, std::ios::out);
if (!jsf.is_open()) {
std::cout << "不能打开文件:" << std::endl;
return false;
}
jsf << json_root.toStyledString();
jsf.close();
return true;
}
std::string JsonCommand::get_command(const ProjectInfo* proj,
const std::vector<ProjectInfo>& projs) {
std::string result{};
result.append(
R"("C:\PROGRA~2\Microsoft Visual Studio 14.0\VC\bin\amd64\cl.exe")");
std::string co_include = proj->addtion_;
std::string co_predefinitions = proj->predefinition_;
std::string speciaA("$(IncludePath)");
std::string speciaB("%(PreprocessorDefinitions)");
std::string speciaC("%(AdditionalIncludeDirectories)");
std::string speciaD("$(WindowsSDK_IncludePath)");
std::string speciaE("$(VC_IncludePath)");
replaceAll(co_include, speciaA, "");
replaceAll(co_include, speciaC, "");
replaceAll(co_include, speciaD, "");
replaceAll(co_include, speciaE, "");
replaceAll(co_predefinitions, speciaB, "");
auto include_vec = split(co_include, ";");
auto prede_vec = split(co_predefinitions, ";");
for (const auto& str : prede_vec) {
if (str.empty()) {
continue;
}
result.append(" -D" + str);
}
for (const auto& str : include_vec) {
if (str.empty()) {
continue;
}
if (expect_.find(str) != std::string::npos) {
continue;
}
result.append(" -I" + fs::path(proj->proj_root_).append(str).string());
}
std::list<std::string> proj_list{};
std::vector<std::string> re_relate{};
auto handle = [&](const std::vector<std::string>& lrelate) {
for (const auto& str : lrelate) {
proj_list.push_back(str);
}
};
for (const auto& str : proj->guid_relate_) {
// 这里的relate 子项目有别的依赖的话都需要加上
// 这里假定不会出现循环引用的情况 (理论也不应该出现)
for (const auto& pj : projs) {
if (pj.guid_ == str) {
handle(pj.guid_relate_);
re_relate.push_back(pj.path_);
break;
}
}
//std::string dir = fs::path(str).parent_path().string();
//result.append(" -I" + dir);
}
while (!proj_list.empty()) {
std::string& data = proj_list.front();
for (const auto& pj : projs) {
if (data == pj.guid_) {
handle(pj.guid_relate_);
re_relate.push_back(pj.path_);
break;
}
}
proj_list.pop_front();
}
// 去重
std::set<std::string> no_reply{};
for (const auto& str : re_relate) {
no_reply.insert(str);
}
for (const auto& str : no_reply) {
std::string dir = fs::path(str).parent_path().string();
result.append(" -I" + dir);
}
// std::cout << result << std::endl;
return result;
}
std::vector<std::string> JsonCommand::split(const std::string& source,
const std::string& sep) {
std::vector<std::string> tokens;
std::size_t start = 0;
std::size_t end = 0;
while ((end = source.find(sep, start)) != std::string::npos) {
tokens.push_back(source.substr(start, end - start));
start = end + sep.length();
}
tokens.push_back(source.substr(start));
return tokens;
}
std::vector<std::string> JsonCommand::get_cpp_paths(
const std::string& parent_path) {
std::vector<std::string> vec{};
try {
for (const auto& entry : fs::directory_iterator(parent_path)) {
if (!fs::is_regular_file(entry.path())) {
continue;
}
const fs::path& file_path(entry.path());
if (file_path.extension().string() == ".cpp") {
vec.push_back(file_path.string());
}
}
} catch (fs::filesystem_error& e) {
std::cout << "Error: " << e.what() << std::endl;
}
return vec;
}
void JsonCommand::replaceAll(std::string& str, const std::string& from,
const std::string& to) {
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}

26
vs_json.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <json/json.h>
#include "vs_generate.h"
class JsonCommand {
public:
JsonCommand();
~JsonCommand();
public:
bool generate(const std::vector<ProjectInfo>& projs);
void set_expect(const std::string& exp);
void set_out(const std::string& out);
private:
std::string expect_{"expect"};
std::string out_{"compile_commands.json" };
std::string get_command(const ProjectInfo* proj, const std::vector<ProjectInfo>& projs);
static std::vector<std::string> get_cpp_paths(const std::string& parent_path);
static std::vector<std::string> split(const std::string& source,
const std::string& sep);
static void replaceAll(std::string& str, const std::string& from,
const std::string& to);
};