mirror of https://gitlab.com/cppit/jucipp
24 changed files with 545 additions and 63 deletions
@ -0,0 +1,79 @@ |
|||||||
|
#include "compile_commands.h" |
||||||
|
#include <boost/property_tree/json_parser.hpp> |
||||||
|
|
||||||
|
std::vector<std::string> CompileCommands::Command::paramter_values(const std::string ¶mter_name) const { |
||||||
|
std::vector<std::string> parameter_values; |
||||||
|
|
||||||
|
bool found_argument=false; |
||||||
|
for(auto ¶meter: parameters) { |
||||||
|
if(found_argument) { |
||||||
|
parameter_values.emplace_back(parameter); |
||||||
|
found_argument=false; |
||||||
|
} |
||||||
|
else if(parameter==paramter_name) |
||||||
|
found_argument=true; |
||||||
|
} |
||||||
|
|
||||||
|
return parameter_values; |
||||||
|
} |
||||||
|
|
||||||
|
CompileCommands::CompileCommands(const boost::filesystem::path &build_path) { |
||||||
|
try { |
||||||
|
boost::property_tree::ptree root_pt; |
||||||
|
boost::property_tree::json_parser::read_json((build_path/"compile_commands.json").string(), root_pt); |
||||||
|
|
||||||
|
auto commands_pt=root_pt.get_child(""); |
||||||
|
for(auto &command: commands_pt) { |
||||||
|
boost::filesystem::path directory=command.second.get<std::string>("directory"); |
||||||
|
auto parameters_str=command.second.get<std::string>("command"); |
||||||
|
boost::filesystem::path file=command.second.get<std::string>("file"); |
||||||
|
|
||||||
|
std::vector<std::string> parameters; |
||||||
|
bool backslash=false; |
||||||
|
bool single_quote=false; |
||||||
|
bool double_quote=false; |
||||||
|
size_t parameter_start_pos=-1; |
||||||
|
size_t parameter_size=0; |
||||||
|
auto add_parameter=[¶meters, ¶meters_str, ¶meter_start_pos, ¶meter_size] { |
||||||
|
auto parameter=parameters_str.substr(parameter_start_pos, parameter_size); |
||||||
|
// Remove escaping
|
||||||
|
for(size_t c=0;c<parameter.size()-1;++c) { |
||||||
|
if(parameter[c]=='\\') |
||||||
|
parameter.replace(c, 2, std::string()+parameter[c+1]); |
||||||
|
} |
||||||
|
parameters.emplace_back(parameter); |
||||||
|
}; |
||||||
|
for(size_t c=0;c<parameters_str.size();++c) { |
||||||
|
if(backslash) |
||||||
|
backslash=false; |
||||||
|
else if(parameters_str[c]=='\\') |
||||||
|
backslash=true; |
||||||
|
else if((parameters_str[c]==' ' || parameters_str[c]=='\t') && !backslash && !single_quote && !double_quote) { |
||||||
|
if(parameter_start_pos!=static_cast<size_t>(-1)) { |
||||||
|
add_parameter(); |
||||||
|
parameter_start_pos=-1; |
||||||
|
parameter_size=0; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
else if(parameters_str[c]=='\'' && !backslash && !double_quote) { |
||||||
|
single_quote=!single_quote; |
||||||
|
continue; |
||||||
|
} |
||||||
|
else if(parameters_str[c]=='\"' && !backslash && !single_quote) { |
||||||
|
double_quote=!double_quote; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if(parameter_start_pos==static_cast<size_t>(-1)) |
||||||
|
parameter_start_pos=c; |
||||||
|
++parameter_size; |
||||||
|
} |
||||||
|
if(parameter_start_pos!=static_cast<size_t>(-1)) |
||||||
|
add_parameter(); |
||||||
|
|
||||||
|
commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)}); |
||||||
|
} |
||||||
|
} |
||||||
|
catch(...) {} |
||||||
|
} |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
#ifndef JUCI_COMPILE_COMMANDS_H_ |
||||||
|
#define JUCI_COMPILE_COMMANDS_H_ |
||||||
|
|
||||||
|
#include <boost/filesystem.hpp> |
||||||
|
#include <vector> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
class CompileCommands { |
||||||
|
public: |
||||||
|
class Command { |
||||||
|
public: |
||||||
|
boost::filesystem::path directory; |
||||||
|
std::vector<std::string> parameters; |
||||||
|
boost::filesystem::path file; |
||||||
|
|
||||||
|
std::vector<std::string> paramter_values(const std::string ¶meter_name) const; |
||||||
|
}; |
||||||
|
|
||||||
|
CompileCommands(const boost::filesystem::path &build_path); |
||||||
|
std::vector<Command> commands; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // JUCI_COMPILE_COMMANDS_H_
|
||||||
@ -0,0 +1,130 @@ |
|||||||
|
#include "meson.h" |
||||||
|
#include "filesystem.h" |
||||||
|
#include "compile_commands.h" |
||||||
|
#include <regex> |
||||||
|
#include "terminal.h" |
||||||
|
#include "dialogs.h" |
||||||
|
#include "config.h" |
||||||
|
|
||||||
|
Meson::Meson(const boost::filesystem::path &path) { |
||||||
|
const auto find_project=[this](const boost::filesystem::path &file_path) { |
||||||
|
for(auto &line: filesystem::read_lines(file_path)) { |
||||||
|
const static std::regex project_regex("^ *project *\\(.*$", std::regex::icase); |
||||||
|
std::smatch sm; |
||||||
|
if(std::regex_match(line, sm, project_regex)) |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); |
||||||
|
while(true) { |
||||||
|
auto search_file=search_path/"meson.build"; |
||||||
|
if(boost::filesystem::exists(search_file)) { |
||||||
|
if(find_project(search_file)) { |
||||||
|
project_path=search_path; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if(search_path==search_path.root_directory()) |
||||||
|
break; |
||||||
|
search_path=search_path.parent_path(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Meson::update_default_build(const boost::filesystem::path &default_build_path, bool force) { |
||||||
|
if(project_path.empty()) |
||||||
|
return false; |
||||||
|
|
||||||
|
if(!boost::filesystem::exists(project_path/"meson.build")) |
||||||
|
return false; |
||||||
|
|
||||||
|
if(default_build_path.empty()) |
||||||
|
return false; |
||||||
|
if(!boost::filesystem::exists(default_build_path)) { |
||||||
|
boost::system::error_code ec; |
||||||
|
boost::filesystem::create_directories(default_build_path, ec); |
||||||
|
if(ec) { |
||||||
|
Terminal::get().print("Error: could not create "+default_build_path.string()+": "+ec.message()+"\n", true); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
auto compile_commands_path=default_build_path/"compile_commands.json"; |
||||||
|
bool compile_commands_exists=boost::filesystem::exists(compile_commands_path); |
||||||
|
if(!force && compile_commands_exists) |
||||||
|
return true; |
||||||
|
|
||||||
|
Dialog::Message message("Creating/updating default build"); |
||||||
|
auto exit_status=Terminal::get().process(Config::get().project.meson.command+' '+(compile_commands_exists?"--internal regenerate ":"")+ |
||||||
|
filesystem::escape_argument(project_path), default_build_path); |
||||||
|
message.hide(); |
||||||
|
if(exit_status==EXIT_SUCCESS) |
||||||
|
return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
bool Meson::update_debug_build(const boost::filesystem::path &debug_build_path, bool force) { |
||||||
|
if(project_path.empty()) |
||||||
|
return false; |
||||||
|
|
||||||
|
if(!boost::filesystem::exists(project_path/"meson.build")) |
||||||
|
return false; |
||||||
|
|
||||||
|
if(debug_build_path.empty()) |
||||||
|
return false; |
||||||
|
if(!boost::filesystem::exists(debug_build_path)) { |
||||||
|
boost::system::error_code ec; |
||||||
|
boost::filesystem::create_directories(debug_build_path, ec); |
||||||
|
if(ec) { |
||||||
|
Terminal::get().print("Error: could not create "+debug_build_path.string()+": "+ec.message()+"\n", true); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool compile_commands_exists=boost::filesystem::exists(debug_build_path/"compile_commands.json"); |
||||||
|
if(!force && compile_commands_exists) |
||||||
|
return true; |
||||||
|
|
||||||
|
Dialog::Message message("Creating/updating debug build"); |
||||||
|
auto exit_status=Terminal::get().process(Config::get().project.meson.command+' '+(compile_commands_exists?"--internal regenerate ":"")+ |
||||||
|
"--buildtype debug "+filesystem::escape_argument(project_path), debug_build_path); |
||||||
|
message.hide(); |
||||||
|
if(exit_status==EXIT_SUCCESS) |
||||||
|
return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
boost::filesystem::path Meson::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { |
||||||
|
CompileCommands compile_commands(build_path); |
||||||
|
|
||||||
|
size_t best_match_size=-1; |
||||||
|
boost::filesystem::path best_match_executable; |
||||||
|
for(auto &command: compile_commands.commands) { |
||||||
|
boost::system::error_code ec; |
||||||
|
auto command_file=boost::filesystem::canonical(command.file, ec); |
||||||
|
if(!ec) { |
||||||
|
auto values=command.paramter_values("-o"); |
||||||
|
if(!values.empty()) { |
||||||
|
size_t pos; |
||||||
|
if((pos=values[0].find("@"))!=std::string::npos) { |
||||||
|
if(pos+1<values[0].size() && values[0].substr(pos+1, 3)=="exe") { |
||||||
|
auto executable=build_path/values[0].substr(0, pos); |
||||||
|
if(command_file==file_path) |
||||||
|
return executable; |
||||||
|
auto command_file_directory=command_file.parent_path(); |
||||||
|
if(filesystem::file_in_path(file_path, command_file_directory)) { |
||||||
|
auto size=command_file_directory.string().size(); |
||||||
|
if(best_match_size==static_cast<size_t>(-1) || best_match_size<size) { |
||||||
|
best_match_size=size; |
||||||
|
best_match_executable=executable; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return best_match_executable; |
||||||
|
} |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
#ifndef JUCI_MESON_H_ |
||||||
|
#define JUCI_MESON_H_ |
||||||
|
|
||||||
|
#include <boost/filesystem.hpp> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
class Meson { |
||||||
|
public: |
||||||
|
Meson(const boost::filesystem::path &path); |
||||||
|
|
||||||
|
boost::filesystem::path project_path; |
||||||
|
|
||||||
|
bool update_default_build(const boost::filesystem::path &default_build_path, bool force=false); |
||||||
|
bool update_debug_build(const boost::filesystem::path &debug_build_path, bool force=false); |
||||||
|
|
||||||
|
boost::filesystem::path get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif //JUCI_MESON_H_
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
#include "compile_commands.h" |
||||||
|
#include <glib.h> |
||||||
|
|
||||||
|
int main() { |
||||||
|
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); |
||||||
|
|
||||||
|
{ |
||||||
|
CompileCommands compile_commands(tests_path/"meson_test_files"/"build"); |
||||||
|
|
||||||
|
g_assert(compile_commands.commands.at(0).directory=="jucipp/tests/meson_test_files/build"); |
||||||
|
|
||||||
|
g_assert_cmpuint(compile_commands.commands.size(), ==, 5); |
||||||
|
|
||||||
|
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(0).c_str(), ==, "te's\"t"); |
||||||
|
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(1).c_str(), ==, "te st"); |
||||||
|
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(2).c_str(), ==, "test"); |
||||||
|
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(3).c_str(), ==, "te\\st"); |
||||||
|
g_assert_cmpstr(compile_commands.commands.at(4).parameters.at(4).c_str(), ==, "te\\\\st"); |
||||||
|
|
||||||
|
auto parameter_values=compile_commands.commands.at(0).paramter_values("-o"); |
||||||
|
g_assert_cmpuint(parameter_values.size(), ==, 1); |
||||||
|
g_assert_cmpstr(parameter_values.at(0).c_str(), ==, "hello_lib@sta/main.cpp.o"); |
||||||
|
|
||||||
|
g_assert(boost::filesystem::canonical(compile_commands.commands.at(0).file) == tests_path/"meson_test_files"/"main.cpp"); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
CompileCommands compile_commands(tests_path/"source_clang_test_files"/"build"); |
||||||
|
|
||||||
|
g_assert(compile_commands.commands.at(0).directory=="build"); |
||||||
|
|
||||||
|
g_assert_cmpuint(compile_commands.commands.size(), ==, 1); |
||||||
|
|
||||||
|
g_assert_cmpstr(compile_commands.commands.at(0).parameters.at(2).c_str(), ==, "-Wall"); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
#include "meson.h" |
||||||
|
#include <glib.h> |
||||||
|
#include "project.h" |
||||||
|
|
||||||
|
int main() { |
||||||
|
auto tests_path=boost::filesystem::canonical(JUCI_TESTS_PATH); |
||||||
|
auto meson_test_files_path=boost::filesystem::canonical(tests_path/"meson_test_files"); |
||||||
|
|
||||||
|
{ |
||||||
|
Meson meson(meson_test_files_path/"a_subdir"); |
||||||
|
g_assert(meson.project_path==meson_test_files_path); |
||||||
|
} |
||||||
|
|
||||||
|
{ |
||||||
|
Meson meson(meson_test_files_path); |
||||||
|
g_assert(meson.project_path==meson_test_files_path); |
||||||
|
|
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"main.cpp")==meson_test_files_path/"build"/"hello"); |
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"another_file.cpp")==meson_test_files_path/"build"/"another_executable"); |
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir"/"main.cpp")==meson_test_files_path/"build"/"a_subdir"/"hello2"); |
||||||
|
|
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"non_existing_file.cpp")==meson_test_files_path/"build"/"hello"); |
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path)==meson_test_files_path/"build"/"hello"); |
||||||
|
|
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir")==meson_test_files_path/"build"/"a_subdir"/"hello2"); |
||||||
|
g_assert(meson.get_executable(meson_test_files_path/"build", meson_test_files_path/"a_subdir"/"non_existing_file.cpp")==meson_test_files_path/"build"/"a_subdir"/"hello2"); |
||||||
|
} |
||||||
|
|
||||||
|
auto build=Project::Build::create(meson_test_files_path); |
||||||
|
g_assert(dynamic_cast<Project::MesonBuild*>(build.get())); |
||||||
|
|
||||||
|
build=Project::Build::create(meson_test_files_path/"a_subdir"); |
||||||
|
g_assert(dynamic_cast<Project::MesonBuild*>(build.get())); |
||||||
|
} |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#include <iostream> |
||||||
|
|
||||||
|
int main() { |
||||||
|
std::cout << "Hello World\n"; |
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
executable('hello2', 'main.cpp', cpp_args: compiler_args) |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#include <iostream> |
||||||
|
|
||||||
|
int main() { |
||||||
|
std::cout << "Hello World\n"; |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"directory": "jucipp/tests/meson_test_files/build", |
||||||
|
"command": "c++ '-Ihello_lib@sta' '-I..' '-I.' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-std=c++11' '-Wall' '-Wextra' '-O0' '-g' '-MMD' '-MQ' 'hello_lib@sta/main.cpp.o' '-MF' 'hello_lib@sta/main.cpp.o.d' -o 'hello_lib@sta/main.cpp.o' -c ../main.cpp", |
||||||
|
"file": "../main.cpp" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"directory": "jucipp/tests/meson_test_files/build", |
||||||
|
"command": "c++ '-Ihello@exe' '-I..' '-I.' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-std=c++11' '-Wall' '-Wextra' '-O0' '-g' '-MMD' '-MQ' 'hello@exe/main.cpp.o' '-MF' 'hello@exe/main.cpp.o.d' -o 'hello@exe/main.cpp.o' -c ../main.cpp", |
||||||
|
"file": "../main.cpp" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"directory": "jucipp/tests/meson_test_files/build", |
||||||
|
"command": "c++ '-Ia_subdir/hello2@exe' '-I../a_subdir' '-Ia_subdir' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-std=c++11' '-Wall' '-Wextra' '-O0' '-g' '-MMD' '-MQ' 'a_subdir/hello2@exe/main.cpp.o' '-MF' 'a_subdir/hello2@exe/main.cpp.o.d' -o 'a_subdir/hello2@exe/main.cpp.o' -c ../a_subdir/main.cpp", |
||||||
|
"file": "../a_subdir/main.cpp" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"directory": "jucipp/tests/meson_test_files/build", |
||||||
|
"command": "c++ '-Ianother_executable@exe' '-I..' '-I.' '-Wall' '-Winvalid-pch' '-Wnon-virtual-dtor' '-std=c++11' '-Wall' '-Wextra' '-O0' '-g' '-MMD' '-MQ' 'another_executable@exe/another_file.cpp.o' '-MF' 'another_executable@exe/another_file.cpp.o.d' -o 'another_executable@exe/another_file.cpp.o' -c ../another_file.cpp", |
||||||
|
"file": "../another_file.cpp" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"directory": "jucipp/tests/meson_test_files/build", |
||||||
|
"command": "'te\\'s\"t' te\\ st test te\\\\st te\\\\\\\\st", |
||||||
|
"file": "../parse_test.cpp" |
||||||
|
} |
||||||
|
] |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#include <iostream> |
||||||
|
|
||||||
|
int main() { |
||||||
|
std::cout << "Hello World\n"; |
||||||
|
} |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
project('hello.world', 'cpp') |
||||||
|
|
||||||
|
compiler_args = ['-std=c++11', '-Wall', '-Wextra'] |
||||||
|
|
||||||
|
static_library('hello_lib', 'main.cpp', cpp_args: compiler_args) |
||||||
|
|
||||||
|
executable('hello', 'main.cpp', cpp_args: compiler_args) |
||||||
|
|
||||||
|
executable('another_executable', 'another_file.cpp', cpp_args: compiler_args) |
||||||
|
|
||||||
|
subdir('a_subdir') |
||||||
Loading…
Reference in new issue