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