diff --git a/CMakeLists.txt b/CMakeLists.txt index f91b9a4..45cf346 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required (VERSION 2.8.8) project(juci) -set(JUCI_VERSION "1.2.2") +set(JUCI_VERSION "1.2.2.1") set(CPACK_PACKAGE_NAME "jucipp") set(CPACK_PACKAGE_CONTACT "Ole Christian Eidheim ") diff --git a/README.md b/README.md index 217df51..6bb0978 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ towards libclang with speed, stability, and ease of use in mind. * C++ warnings and errors on the fly * C++ Fix-its * Debug integration, both local and remote, through lldb -* Automated CMake processing, including support for external libraries +* Supports the following build systems: + * CMake + * Meson * Git support through libgit2 * Fast C++ autocompletion * Keyword and buffer autocompletion for other file types diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a92ddff..fd17bb6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,10 +22,12 @@ set(project_files #Files used both in ../src and ../tests set(project_shared_files cmake.cc + compile_commands.cc ctags.cc dispatcher.cc filesystem.cc git.cc + meson.cc project_build.cc source.cc source_clang.cc diff --git a/src/cmake.cc b/src/cmake.cc index e7be043..0678622 100644 --- a/src/cmake.cc +++ b/src/cmake.cc @@ -55,7 +55,7 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa auto compile_commands_path=default_build_path/"compile_commands.json"; Dialog::Message message("Creating/updating default build"); - auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ + auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ filesystem::escape_argument(project_path)+" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON", default_build_path); message.hide(); if(exit_status==EXIT_SUCCESS) { @@ -102,7 +102,7 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path, return true; Dialog::Message message("Creating/updating debug build"); - auto exit_status=Terminal::get().process(Config::get().project.cmake_command+" "+ + auto exit_status=Terminal::get().process(Config::get().project.cmake.command+' '+ filesystem::escape_argument(project_path)+" -DCMAKE_BUILD_TYPE=Debug", debug_build_path); message.hide(); if(exit_status==EXIT_SUCCESS) diff --git a/src/cmake.h b/src/cmake.h index 004688d..c345c46 100644 --- a/src/cmake.h +++ b/src/cmake.h @@ -9,15 +9,13 @@ class CMake { public: CMake(const boost::filesystem::path &path); boost::filesystem::path project_path; - std::vector paths; 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 &file_path); - - std::vector > > get_functions_parameters(const std::string &name); private: + std::vector paths; std::vector files; std::unordered_map variables; void read_files(); @@ -28,6 +26,7 @@ private: void parse_variable_parameters(std::string &data); void parse(); std::vector get_function_parameters(std::string &data); + std::vector > > get_functions_parameters(const std::string &name); bool parsed=false; }; #endif //JUCI_CMAKE_H_ diff --git a/src/compile_commands.cc b/src/compile_commands.cc new file mode 100644 index 0000000..d3f6a38 --- /dev/null +++ b/src/compile_commands.cc @@ -0,0 +1,79 @@ +#include "compile_commands.h" +#include + +std::vector CompileCommands::Command::paramter_values(const std::string ¶mter_name) const { + std::vector 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("directory"); + auto parameters_str=command.second.get("command"); + boost::filesystem::path file=command.second.get("file"); + + std::vector 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(-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(-1)) + parameter_start_pos=c; + ++parameter_size; + } + if(parameter_start_pos!=static_cast(-1)) + add_parameter(); + + commands.emplace_back(Command{directory, parameters, boost::filesystem::absolute(file, build_path)}); + } + } + catch(...) {} +} diff --git a/src/compile_commands.h b/src/compile_commands.h new file mode 100644 index 0000000..3747651 --- /dev/null +++ b/src/compile_commands.h @@ -0,0 +1,23 @@ +#ifndef JUCI_COMPILE_COMMANDS_H_ +#define JUCI_COMPILE_COMMANDS_H_ + +#include +#include +#include + +class CompileCommands { +public: + class Command { + public: + boost::filesystem::path directory; + std::vector parameters; + boost::filesystem::path file; + + std::vector paramter_values(const std::string ¶meter_name) const; + }; + + CompileCommands(const boost::filesystem::path &build_path); + std::vector commands; +}; + +#endif // JUCI_COMPILE_COMMANDS_H_ \ No newline at end of file diff --git a/src/config.cc b/src/config.cc index 790a890..0b56eb9 100644 --- a/src/config.cc +++ b/src/config.cc @@ -90,8 +90,10 @@ void Config::retrieve_config() { project.default_build_path=cfg.get("project.default_build_path"); project.debug_build_path=cfg.get("project.debug_build_path"); - project.make_command=cfg.get("project.make_command"); - project.cmake_command=cfg.get("project.cmake_command"); + project.cmake.command=cfg.get("project.cmake.command"); + project.cmake.compile_command=cfg.get("project.cmake.compile_command"); + project.meson.command=cfg.get("project.meson.command"); + project.meson.compile_command=cfg.get("project.meson.compile_command"); project.save_on_compile_or_run=cfg.get("project.save_on_compile_or_run"); project.clear_terminal_on_compile=cfg.get("project.clear_terminal_on_compile"); project.ctags_command=cfg.get("project.ctags_command"); diff --git a/src/config.h b/src/config.h index 6e4f8c9..581e566 100644 --- a/src/config.h +++ b/src/config.h @@ -36,10 +36,21 @@ public: class Project { public: + class CMake { + public: + std::string command; + std::string compile_command; + }; + class Meson { + public: + std::string command; + std::string compile_command; + }; + std::string default_build_path; std::string debug_build_path; - std::string cmake_command; - std::string make_command; + CMake cmake; + Meson meson; bool save_on_compile_or_run; bool clear_terminal_on_compile; std::string ctags_command; diff --git a/src/files.h b/src/files.h index 74ce62e..dd98858 100644 --- a/src/files.h +++ b/src/files.h @@ -156,16 +156,29 @@ R"RAW( "default_build_path_comment": "Use to insert the project top level directory name", "default_build_path": "./build", "debug_build_path_comment": "Use to insert the project top level directory name, and to insert your default_build_path setting.", - "debug_build_path": "/debug",)RAW" + "debug_build_path": "/debug", + "cmake": {)RAW" #ifdef _WIN32 R"RAW( - "cmake_command": "cmake -G\"MSYS Makefiles\"",)RAW" + "command": "cmake -G\"MSYS Makefiles\"",)RAW" #else R"RAW( - "cmake_command": "cmake",)RAW" + "command": "cmake",)RAW" #endif R"RAW( - "make_command": "cmake --build .", + "compile_command": "cmake --build ." + }, + "meson": {)RAW" +#ifdef __APPLE__ +R"RAW( + "command": "meson.py",)RAW" +#else +R"RAW( + "command": "meson",)RAW" +#endif +R"RAW( + "compile_command": "ninja" + }, "save_on_compile_or_run": true, "clear_terminal_on_compile": true, "ctags_command": "ctags" diff --git a/src/meson.cc b/src/meson.cc new file mode 100644 index 0000000..ccab7e1 --- /dev/null +++ b/src/meson.cc @@ -0,0 +1,130 @@ +#include "meson.h" +#include "filesystem.h" +#include "compile_commands.h" +#include +#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(-1) || best_match_size +#include + +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_ diff --git a/src/project.cc b/src/project.cc index 9c4eb7a..05c0559 100644 --- a/src/project.cc +++ b/src/project.cc @@ -42,26 +42,32 @@ void Project::on_save(size_t index) { auto view=Notebook::get().get_view(index); if(!view) return; + boost::filesystem::path build_path; if(view->language && view->language->get_id()=="cmake") { - boost::filesystem::path cmake_path; if(view->file_path.filename()=="CMakeLists.txt") - cmake_path=view->file_path; + build_path=view->file_path; else - cmake_path=filesystem::find_file_in_path_parents("CMakeLists.txt", view->file_path.parent_path()); - - if(!cmake_path.empty()) { - auto build=Build::create(cmake_path); - if(dynamic_cast(build.get())) { - build->update_default(true); - if(boost::filesystem::exists(build->get_debug_path())) - build->update_debug(true); - - for(size_t c=0;c(source_view)) { - if(filesystem::file_in_path(source_clang_view->file_path, build->project_path)) - source_clang_view->full_reparse_needed=true; - } + build_path=filesystem::find_file_in_path_parents("CMakeLists.txt", view->file_path.parent_path()); + } + else if(view->language && view->language->get_id()=="meson") { + if(view->file_path.filename()=="meson.build") + build_path=view->file_path; + else + build_path=filesystem::find_file_in_path_parents("meson.build", view->file_path.parent_path()); + } + + if(!build_path.empty()) { + auto build=Build::create(build_path); + if(dynamic_cast(build.get()) || dynamic_cast(build.get())) { + build->update_default(true); + if(boost::filesystem::exists(build->get_debug_path())) + build->update_debug(true); + + for(size_t c=0;c(source_view)) { + if(filesystem::file_in_path(source_clang_view->file_path, build->project_path)) + source_clang_view->full_reparse_needed=true; } } } @@ -140,7 +146,7 @@ std::unique_ptr Project::create() { else build=Build::create(Directories::get().path); - if(dynamic_cast(build.get())) + if(dynamic_cast(build.get()) || dynamic_cast(build.get())) return std::unique_ptr(new Project::Clang(std::move(build))); else return std::unique_ptr(new Project::Base(std::move(build))); @@ -213,14 +219,10 @@ std::pair Project::Clang::get_run_arguments() { if(arguments.empty()) { auto view=Notebook::get().get_current_view(); - auto executable=build->get_executable(view?view->file_path:"").string(); + auto executable=build->get_executable(view?view->file_path:Directories::get().path).string(); - if(executable!="") { - size_t pos=executable.find(project_path); - if(pos!=std::string::npos) - executable.replace(pos, project_path.size(), build_path.string()); + if(!executable.empty()) arguments=filesystem::escape_argument(executable); - } else arguments=filesystem::escape_argument(build->get_default_path()); } @@ -238,7 +240,12 @@ void Project::Clang::compile() { compiling=true; Terminal::get().print("Compiling project "+build->project_path.string()+"\n"); - Terminal::get().async_process(Config::get().project.make_command, default_build_path, [this](int exit_status) { + std::string compile_command; + if(dynamic_cast(build.get())) + compile_command=Config::get().project.cmake.compile_command; + else if(dynamic_cast(build.get())) + compile_command=Config::get().project.meson.compile_command; + Terminal::get().async_process(compile_command, default_build_path, [this](int exit_status) { compiling=false; }); } @@ -257,15 +264,12 @@ void Project::Clang::compile_and_run() { if(arguments.empty()) { auto view=Notebook::get().get_current_view(); - arguments=build->get_executable(view?view->file_path:"").string(); + arguments=build->get_executable(view?view->file_path:Directories::get().path).string(); if(arguments.empty()) { Terminal::get().print("Warning: could not find executable.\n"); - Terminal::get().print("Solution: either use Project Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); + Terminal::get().print("Solution: either use Project Set Run Arguments, or open a source file within a directory where an executable is defined.\n", true); return; } - size_t pos=arguments.find(project_path.string()); - if(pos!=std::string::npos) - arguments.replace(pos, project_path.string().size(), default_build_path.string()); arguments=filesystem::escape_argument(arguments); } @@ -274,7 +278,12 @@ void Project::Clang::compile_and_run() { compiling=true; Terminal::get().print("Compiling and running "+arguments+"\n"); - Terminal::get().async_process(Config::get().project.make_command, default_build_path, [this, arguments, project_path](int exit_status){ + std::string compile_command; + if(dynamic_cast(build.get())) + compile_command=Config::get().project.cmake.compile_command; + else if(dynamic_cast(build.get())) + compile_command=Config::get().project.meson.compile_command; + Terminal::get().async_process(compile_command, default_build_path, [this, arguments, project_path](int exit_status){ compiling=false; if(exit_status==EXIT_SUCCESS) { Terminal::get().async_process(arguments, project_path, [this, arguments](int exit_status){ @@ -339,8 +348,9 @@ void Project::Clang::recreate_build() { #ifdef JUCI_ENABLE_DEBUG std::pair Project::Clang::debug_get_run_arguments() { - auto build_path=build->get_debug_path(); - if(build_path.empty()) + auto debug_build_path=build->get_debug_path(); + auto default_build_path=build->get_default_path(); + if(debug_build_path.empty()) return {"", ""}; auto project_path=build->project_path.string(); @@ -351,12 +361,12 @@ std::pair Project::Clang::debug_get_run_arguments() { if(arguments.empty()) { auto view=Notebook::get().get_current_view(); - auto executable=build->get_executable(view?view->file_path:"").string(); + auto executable=build->get_executable(view?view->file_path:Directories::get().path).string(); - if(executable!="") { - size_t pos=executable.find(project_path); + if(!executable.empty()) { + size_t pos=executable.find(default_build_path.string()); if(pos!=std::string::npos) - executable.replace(pos, project_path.size(), build_path.string()); + executable.replace(pos, default_build_path.string().size(), debug_build_path.string()); arguments=filesystem::escape_argument(executable); } else @@ -376,6 +386,9 @@ void Project::Clang::debug_start() { auto debug_build_path=build->get_debug_path(); if(debug_build_path.empty() || !build->update_debug()) return; + auto default_build_path=build->get_default_path(); + if(default_build_path.empty()) + return; auto project_path=std::make_shared(build->project_path); auto run_arguments_it=debug_run_arguments.find(project_path->string()); @@ -385,15 +398,15 @@ void Project::Clang::debug_start() { if(run_arguments->empty()) { auto view=Notebook::get().get_current_view(); - *run_arguments=build->get_executable(view?view->file_path:"").string(); + *run_arguments=build->get_executable(view?view->file_path:Directories::get().path).string(); if(run_arguments->empty()) { Terminal::get().print("Warning: could not find executable.\n"); - Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where add_executable is set.\n", true); + Terminal::get().print("Solution: either use Debug Set Run Arguments, or open a source file within a directory where an executable is defined.\n", true); return; } - size_t pos=run_arguments->find(project_path->string()); + size_t pos=run_arguments->find(default_build_path.string()); if(pos!=std::string::npos) - run_arguments->replace(pos, project_path->string().size(), debug_build_path.string()); + run_arguments->replace(pos, default_build_path.string().size(), debug_build_path.string()); *run_arguments=filesystem::escape_argument(*run_arguments); } @@ -402,7 +415,12 @@ void Project::Clang::debug_start() { debugging=true; Terminal::get().print("Compiling and debugging "+*run_arguments+"\n"); - Terminal::get().async_process(Config::get().project.make_command, debug_build_path, [this, run_arguments, project_path](int exit_status){ + std::string compile_command; + if(dynamic_cast(build.get())) + compile_command=Config::get().project.cmake.compile_command; + else if(dynamic_cast(build.get())) + compile_command=Config::get().project.meson.compile_command; + Terminal::get().async_process(compile_command, debug_build_path, [this, run_arguments, project_path](int exit_status){ if(exit_status!=EXIT_SUCCESS) debugging=false; else { diff --git a/src/project_build.cc b/src/project_build.cc index afdc6ee..3e3c12a 100644 --- a/src/project_build.cc +++ b/src/project_build.cc @@ -2,11 +2,29 @@ #include "config.h" std::unique_ptr Project::Build::create(const boost::filesystem::path &path) { - std::unique_ptr cmake(new CMakeBuild(path)); - if(!cmake->project_path.empty()) - return cmake; - else - return std::make_unique(); + auto search_path=boost::filesystem::is_directory(path)?path:path.parent_path(); + + while(true) { + if(boost::filesystem::exists(search_path/"CMakeLists.txt")) { + std::unique_ptr cmake(new CMakeBuild(path)); + if(!cmake->project_path.empty()) + return cmake; + else + return std::make_unique(); + } + + if(boost::filesystem::exists(search_path/"meson.build")) { + std::unique_ptr meson(new MesonBuild(path)); + if(!meson->project_path.empty()) + return meson; + } + + if(search_path==search_path.root_directory()) + break; + search_path=search_path.parent_path(); + } + + return std::make_unique(); } boost::filesystem::path Project::Build::get_default_path() { @@ -79,5 +97,25 @@ bool Project::CMakeBuild::update_debug(bool force) { } boost::filesystem::path Project::CMakeBuild::get_executable(const boost::filesystem::path &path) { - return cmake.get_executable(path); + auto executable=cmake.get_executable(path).string(); + size_t pos=executable.find(project_path.string()); + if(pos!=std::string::npos) + executable.replace(pos, project_path.string().size(), get_default_path().string()); + return executable; +} + +Project::MesonBuild::MesonBuild(const boost::filesystem::path &path) : Project::Build(), meson(path) { + project_path=meson.project_path; +} + +bool Project::MesonBuild::update_default(bool force) { + return meson.update_default_build(get_default_path(), force); +} + +bool Project::MesonBuild::update_debug(bool force) { + return meson.update_debug_build(get_debug_path(), force); +} + +boost::filesystem::path Project::MesonBuild::get_executable(const boost::filesystem::path &path) { + return meson.get_executable(get_default_path(), path); } diff --git a/src/project_build.h b/src/project_build.h index 964069f..6c476e4 100644 --- a/src/project_build.h +++ b/src/project_build.h @@ -3,6 +3,7 @@ #include #include "cmake.h" +#include "meson.h" namespace Project { class Build { @@ -32,6 +33,17 @@ namespace Project { boost::filesystem::path get_executable(const boost::filesystem::path &path) override; }; + + class MesonBuild : public Build { + Meson meson; + public: + MesonBuild(const boost::filesystem::path &path); + + bool update_default(bool force=false) override; + bool update_debug(bool force=false) override; + + boost::filesystem::path get_executable(const boost::filesystem::path &path) override; + }; } #endif // JUCI_PROJECT_BUILD_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6116e17..6b659e9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,11 +19,21 @@ add_executable(process_test process_test.cc target_link_libraries(process_test ${global_libraries}) add_test(process_test process_test) +add_executable(compile_commands_test compile_commands_test.cc + $ $) +target_link_libraries(compile_commands_test ${global_libraries}) +add_test(compile_commands_test compile_commands_test) + add_executable(cmake_build_test cmake_build_test.cc $ $) target_link_libraries(cmake_build_test ${global_libraries}) add_test(cmake_build_test cmake_build_test) +add_executable(meson_build_test meson_build_test.cc + $ $) +target_link_libraries(meson_build_test ${global_libraries}) +add_test(meson_build_test meson_build_test) + add_executable(source_test source_test.cc $ $) target_link_libraries(source_test ${global_libraries}) diff --git a/tests/compile_commands_test.cc b/tests/compile_commands_test.cc new file mode 100644 index 0000000..ecfe672 --- /dev/null +++ b/tests/compile_commands_test.cc @@ -0,0 +1,36 @@ +#include "compile_commands.h" +#include + +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"); + } +} diff --git a/tests/meson_build_test.cc b/tests/meson_build_test.cc new file mode 100644 index 0000000..6584287 --- /dev/null +++ b/tests/meson_build_test.cc @@ -0,0 +1,34 @@ +#include "meson.h" +#include +#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(build.get())); + + build=Project::Build::create(meson_test_files_path/"a_subdir"); + g_assert(dynamic_cast(build.get())); +} \ No newline at end of file diff --git a/tests/meson_test_files/a_subdir/main.cpp b/tests/meson_test_files/a_subdir/main.cpp new file mode 100644 index 0000000..1527da0 --- /dev/null +++ b/tests/meson_test_files/a_subdir/main.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + std::cout << "Hello World\n"; +} diff --git a/tests/meson_test_files/a_subdir/meson.build b/tests/meson_test_files/a_subdir/meson.build new file mode 100644 index 0000000..fa1e3aa --- /dev/null +++ b/tests/meson_test_files/a_subdir/meson.build @@ -0,0 +1 @@ +executable('hello2', 'main.cpp', cpp_args: compiler_args) diff --git a/tests/meson_test_files/another_file.cpp b/tests/meson_test_files/another_file.cpp new file mode 100644 index 0000000..1527da0 --- /dev/null +++ b/tests/meson_test_files/another_file.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + std::cout << "Hello World\n"; +} diff --git a/tests/meson_test_files/build/compile_commands.json b/tests/meson_test_files/build/compile_commands.json new file mode 100644 index 0000000..a99e681 --- /dev/null +++ b/tests/meson_test_files/build/compile_commands.json @@ -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" + } +] diff --git a/tests/meson_test_files/main.cpp b/tests/meson_test_files/main.cpp new file mode 100644 index 0000000..1527da0 --- /dev/null +++ b/tests/meson_test_files/main.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + std::cout << "Hello World\n"; +} diff --git a/tests/meson_test_files/meson.build b/tests/meson_test_files/meson.build new file mode 100644 index 0000000..c7a3cb9 --- /dev/null +++ b/tests/meson_test_files/meson.build @@ -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')