From 33510e5bd2e3703292330f958a9b23a347bdbfa5 Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 17 Jun 2020 11:52:15 +0200 Subject: [PATCH] Cleanup of cmake parsing --- src/cmake.cpp | 331 +++++++++++++-------------------- src/cmake.hpp | 21 +-- src/meson.cpp | 2 +- src/notebook.cpp | 15 +- src/project_build.cpp | 6 +- tests/cmake_build_test.cpp | 368 ++++++++++++++++++++++++++++++++++++- 6 files changed, 508 insertions(+), 235 deletions(-) diff --git a/src/cmake.cpp b/src/cmake.cpp index fe85904..04863e8 100644 --- a/src/cmake.cpp +++ b/src/cmake.cpp @@ -5,6 +5,7 @@ #include "filesystem.hpp" #include "terminal.hpp" #include "utility.hpp" +#include #include #include @@ -115,21 +116,6 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui // Therefore, executables are first attempted found in the cmake files. These executables // are then used to identify if a file in compile_commands.json is part of an executable or not - auto parameters = get_functions_parameters("add_executable"); - - std::vector cmake_executables; - for(auto ¶meter : parameters) { - if(parameter.second.size() > 1 && parameter.second[0].size() > 0 && !starts_with(parameter.second[0], "${")) { - auto executable = (parameter.first.parent_path() / parameter.second[0]).string(); - auto project_path_str = project_path.string(); - size_t pos = executable.find(project_path_str); - if(pos != std::string::npos) - executable.replace(pos, project_path_str.size(), build_path.string()); - cmake_executables.emplace_back(executable); - } - } - - CompileCommands compile_commands(build_path); std::vector> command_files_and_maybe_executables; for(auto &command : compile_commands.commands) { @@ -146,6 +132,25 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui } } + std::vector cmake_executables; + + // Parse cmake files + std::map> variables; + for(auto &path : paths) { + parse_file(filesystem::read(path), variables, [this, &build_path, &cmake_executables, &path](Function function) { + if(function.name == "add_executable") { + if(!function.parameters.empty() && !function.parameters.front().empty()) { + auto executable = (path.parent_path() / function.parameters.front()).string(); + auto project_path_str = project_path.string(); + size_t pos = executable.find(project_path_str); + if(pos != std::string::npos) + executable.replace(pos, project_path_str.size(), build_path.string()); + cmake_executables.emplace_back(executable); + } + } + }); + } + boost::optional best_match_size; boost::filesystem::path best_match_executable; @@ -159,7 +164,7 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui auto command_file_directory = command_file.parent_path(); if(filesystem::file_in_path(file_path, command_file_directory)) { auto size = static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size < size) { + if(size > best_match_size) { best_match_size = size; best_match_executable = maybe_executable; } @@ -178,7 +183,7 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui auto command_file_directory = command_file.parent_path(); if(filesystem::file_in_path(file_path, command_file_directory)) { auto size = static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(!best_match_size || best_match_size < size) { + if(size > best_match_size) { best_match_size = size; best_match_executable = maybe_executable; } @@ -187,202 +192,126 @@ boost::filesystem::path CMake::get_executable(const boost::filesystem::path &bui return best_match_executable; } -void CMake::read_files() { - for(auto &path : paths) - files.emplace_back(filesystem::read(path)); -} - -void CMake::remove_tabs() { - for(auto &file : files) { - for(auto &chr : file) { - if(chr == '\t') - chr = ' '; - } - } -} - -void CMake::remove_comments() { - for(auto &file : files) { - size_t pos = 0; - size_t comment_start; - bool inside_comment = false; - while(pos < file.size()) { - if(!inside_comment && file[pos] == '#') { - comment_start = pos; - inside_comment = true; - } - if(inside_comment && file[pos] == '\n') { - file.erase(comment_start, pos - comment_start); - pos -= pos - comment_start; - inside_comment = false; - } - pos++; - } - if(inside_comment) - file.erase(comment_start); - } -} - -void CMake::remove_newlines_inside_parentheses() { - for(auto &file : files) { - size_t pos = 0; - bool inside_para = false; - bool inside_quote = false; - char last_char = 0; - while(pos < file.size()) { - if(!inside_quote && file[pos] == '"' && last_char != '\\') - inside_quote = true; - else if(inside_quote && file[pos] == '"' && last_char != '\\') - inside_quote = false; - - else if(!inside_quote && file[pos] == '(') - inside_para = true; - else if(!inside_quote && file[pos] == ')') - inside_para = false; - - else if(inside_para && file[pos] == '\n') - file.replace(pos, 1, 1, ' '); - last_char = file[pos]; - pos++; - } - } -} - -void CMake::parse_variable_parameters(std::string &data) { - size_t pos = 0; - bool inside_quote = false; - char last_char = 0; - while(pos < data.size()) { - if(!inside_quote && data[pos] == '"' && last_char != '\\') { - inside_quote = true; - data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} - pos--; - } - else if(inside_quote && data[pos] == '"' && last_char != '\\') { - inside_quote = false; - data.erase(pos, 1); //TODO: instead remove quote-mark if pasted into a quote, for instance: "test${test}test"<-remove quotes from ${test} - pos--; - } - else if(!inside_quote && data[pos] == ' ' && pos + 1 < data.size() && data[pos + 1] == ' ') { - data.erase(pos, 1); - pos--; - } +void CMake::parse_file(const std::string &src, std::map> &variables, std::function &&on_function) { + size_t i = 0; - if(pos != static_cast(-1)) - last_char = data[pos]; - pos++; - } - for(auto &var : variables) { - auto pos = data.find("${" + var.first + '}'); - while(pos != std::string::npos) { - data.replace(pos, var.first.size() + 3, var.second); - pos = data.find("${" + var.first + '}'); + auto parse_comment = [&] { + if(src[i] == '#') { + while(i < src.size() && src[i] != '\n') + ++i; + return true; } - } + return false; + }; - //Remove variables we do not know: - pos = data.find("${"); - auto pos_end = data.find('}', pos + 2); - while(pos != std::string::npos && pos_end != std::string::npos) { - data.erase(pos, pos_end - pos + 1); - pos = data.find("${"); - pos_end = data.find('}', pos + 2); - } -} + auto is_whitespace = [&] { + return src[i] == ' ' || src[i] == '\t' || src[i] == '\r' || src[i] == '\n'; + }; -void CMake::parse() { - read_files(); - remove_tabs(); - remove_comments(); - remove_newlines_inside_parentheses(); - parsed = true; -} + auto forward_passed_whitespace = [&] { + while(i < src.size() && is_whitespace()) + ++i; + return i < src.size(); + }; -std::vector CMake::get_function_parameters(std::string &data) { - std::vector parameters; - size_t pos = 0; - size_t parameter_pos = 0; - bool inside_quote = false; - char last_char = 0; - while(pos < data.size()) { - if(!inside_quote && data[pos] == '"' && last_char != '\\') { - inside_quote = true; - data.erase(pos, 1); - pos--; - } - else if(inside_quote && data[pos] == '"' && last_char != '\\') { - inside_quote = false; - data.erase(pos, 1); - pos--; - } - else if(!inside_quote && pos + 1 < data.size() && data[pos] == ' ' && data[pos + 1] == ' ') { - data.erase(pos, 1); - pos--; - } - else if(!inside_quote && data[pos] == ' ') { - parameters.emplace_back(data.substr(parameter_pos, pos - parameter_pos)); - if(pos + 1 < data.size()) - parameter_pos = pos + 1; + auto parse_variable_name = [&]() -> boost::optional { + if(src[i] == '$' && i + 1 < src.size() && src[i + 1] == '{') { + auto start = i + 2; + auto end = src.find('}', start); + if(end != std::string::npos) { + i = end; + auto variable_name = src.substr(start, end - start); + boost::algorithm::to_upper(variable_name); + return variable_name; + } } + return {}; + }; - if(pos != static_cast(-1)) - last_char = data[pos]; - pos++; - } - parameters.emplace_back(data.substr(parameter_pos)); - for(auto &var : variables) { - for(auto ¶meter : parameters) { - auto pos = parameter.find("${" + var.first + '}'); - while(pos != std::string::npos) { - parameter.replace(pos, var.first.size() + 3, var.second); - pos = parameter.find("${" + var.first + '}'); + auto parse_function = [&]() -> boost::optional { + Function function; + if((src[i] >= 'A' && src[i] <= 'Z') || (src[i] >= 'a' && src[i] <= 'z') || src[i] == '_') { + function.name += src[i++]; + while(i < src.size() && ((src[i] >= 'A' && src[i] <= 'Z') || (src[i] >= 'a' && src[i] <= 'z') || (src[i] >= '0' && src[i] <= '9') || src[i] == '_')) + function.name += src[i++]; + if(forward_passed_whitespace() && src[i] == '(') { + ++i; + // Parse parameters + for(; forward_passed_whitespace(); ++i) { + if(src[i] == ')') + return function; + else if(src[i] == '"') { // Parse parameter within "" + std::string parameter; + ++i; + for(; i < src.size(); ++i) { + if(src[i] == '\\' && i + 1 < src.size()) + parameter += src[++i]; + else if(src[i] == '"') + break; + else if(auto variable_name = parse_variable_name()) { + auto it = variables.find(*variable_name); + if(it != variables.end()) { + bool first = true; + for(auto &value : it->second) { + parameter += (first ? "" : ";") + value; + first = false; + } + } + } + else + parameter += src[i]; + } + function.parameters.emplace_back(std::move(parameter)); + } + else { // Parse parameter not within "" + auto parameter_it = function.parameters.end(); + for(; i < src.size() && !is_whitespace() && src[i] != ')'; ++i) { + if(parameter_it == function.parameters.end()) + parameter_it = function.parameters.emplace(parameter_it); + if(src[i] == '\\' && i + 1 < src.size()) + *parameter_it += src[++i]; + else if(auto variable_name = parse_variable_name()) { + auto variable_it = variables.find(*variable_name); + if(variable_it != variables.end()) { + if(variable_it->second.size() == 1) + *parameter_it += variable_it->second.front(); + else if(variable_it->second.size() > 1) { + *parameter_it += variable_it->second.front(); + function.parameters.insert(function.parameters.end(), std::next(variable_it->second.begin()), variable_it->second.end()); + parameter_it = std::prev(function.parameters.end()); + } + } + } + else + *parameter_it += src[i]; + } + if(src[i] == ')') + return function; + } + } } } - } - return parameters; -} + return {}; + }; -std::vector>> CMake::get_functions_parameters(const std::string &name) { - const std::regex function_regex("^ *" + name + R"( *\( *(.*)\) *\r?$)", std::regex::icase); - variables.clear(); - if(!parsed) - parse(); - std::vector>> functions; - for(size_t c = 0; c < files.size(); ++c) { - size_t pos = 0; - while(pos < files[c].size()) { - auto start_line = pos; - auto end_line = files[c].find('\n', start_line); - if(end_line == std::string::npos) - end_line = files[c].size(); - if(end_line > start_line) { - auto line = files[c].substr(start_line, end_line - start_line); - std::smatch sm; - const static std::regex set_regex(R"(^ *set *\( *([A-Za-z_][A-Za-z_0-9]*) +(.*)\) *\r?$)", std::regex::icase); - const static std::regex project_regex(R"(^ *project *\( *([^ ]+).*\) *\r?$)", std::regex::icase); - if(std::regex_match(line, sm, set_regex)) { - auto data = sm[2].str(); - while(data.size() > 0 && data.back() == ' ') - data.pop_back(); - parse_variable_parameters(data); - variables[sm[1].str()] = data; - } - else if(std::regex_match(line, sm, project_regex)) { - auto data = sm[1].str(); - parse_variable_parameters(data); - variables["CMAKE_PROJECT_NAME"] = data; //TODO: is this variable deprecated/non-standard? - variables["PROJECT_NAME"] = data; - } - if(std::regex_match(line, sm, function_regex)) { - auto data = sm[1].str(); - while(data.size() > 0 && data.back() == ' ') - data.pop_back(); - auto parameters = get_function_parameters(data); - functions.emplace_back(paths[c], parameters); + for(; forward_passed_whitespace(); ++i) { + if(parse_comment()) + continue; + if(auto function = parse_function()) { + boost::algorithm::to_lower(function->name); + if(function->name == "set" && !function->parameters.empty() && !function->parameters.front().empty()) { + auto variable_name = std::move(function->parameters.front()); + boost::algorithm::to_upper(variable_name); + function->parameters.erase(function->parameters.begin()); + variables.emplace(std::move(variable_name), std::move(function->parameters)); + } + else if(function->name == "project") { + if(!function->parameters.empty()) { + variables.emplace("CMAKE_PROJECT_NAME", function->parameters); + variables.emplace("PROJECT_NAME", function->parameters); } } - pos = end_line + 1; + on_function(std::move(*function)); } } - return functions; } diff --git a/src/cmake.hpp b/src/cmake.hpp index b60f089..bfc5485 100644 --- a/src/cmake.hpp +++ b/src/cmake.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include -#include +#include +#include #include class CMake { @@ -16,15 +16,10 @@ public: private: std::vector paths; - std::vector files; - std::unordered_map variables; - void read_files(); - void remove_tabs(); - void remove_comments(); - void remove_newlines_inside_parentheses(); - 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; + + struct Function { + std::string name; + std::list parameters; + }; + static void parse_file(const std::string &src, std::map> &variables, std::function &&on_function); }; diff --git a/src/meson.cpp b/src/meson.cpp index 23de259..33c7e0e 100644 --- a/src/meson.cpp +++ b/src/meson.cpp @@ -114,7 +114,7 @@ boost::filesystem::path Meson::get_executable(const boost::filesystem::path &bui auto command_file_directory = command_file.parent_path(); if(filesystem::file_in_path(file_path, command_file_directory)) { auto size = static_cast(std::distance(command_file_directory.begin(), command_file_directory.end())); - if(best_match_size < size) { + if(size > best_match_size) { best_match_size = size; best_match_executable = executable; } diff --git a/src/notebook.cpp b/src/notebook.cpp index 23f2465..2c2e516 100644 --- a/src/notebook.cpp +++ b/src/notebook.cpp @@ -675,10 +675,10 @@ void Notebook::clear_status() { Source::View *Notebook::get_view(size_t notebook_index, int page) { if(notebook_index >= notebooks.size()) - throw "notebook index out of bounds"; + throw std::out_of_range("notebook index out of bounds"); auto widget = notebooks[notebook_index].get_nth_page(page); if(!widget) - throw "page number out of bounds"; + throw std::out_of_range("page number out of bounds"); auto hbox = dynamic_cast(widget); auto scrolled_window = dynamic_cast(hbox->get_children()[0]); return dynamic_cast(scrolled_window->get_children()[0]); @@ -694,7 +694,7 @@ size_t Notebook::get_index(Source::View *view) { if(source_views[c] == view) return c; } - throw "view not found"; + throw std::out_of_range("view not found"); } std::pair Notebook::get_notebook_page(size_t index) { @@ -703,16 +703,11 @@ std::pair Notebook::get_notebook_page(size_t index) { if(page_num >= 0) return {c, page_num}; } - throw "index out of bounds"; + throw std::out_of_range("index out of bounds"); } std::pair Notebook::get_notebook_page(Source::View *view) { - try { - return get_notebook_page(get_index(view)); - } - catch(...) { - throw "view not found"; - } + return get_notebook_page(get_index(view)); } void Notebook::set_current_view(Source::View *view) { diff --git a/src/project_build.cpp b/src/project_build.cpp index 3c5b9a6..ee35548 100644 --- a/src/project_build.cpp +++ b/src/project_build.cpp @@ -112,10 +112,8 @@ boost::filesystem::path Project::CMakeBuild::get_executable(const boost::filesys if(executable.empty()) { auto src_path = project_path / "src"; boost::system::error_code ec; - if(boost::filesystem::is_directory(src_path, ec)) { - auto cmake = CMake(src_path); // ignore cache in this->cmake - executable = cmake.get_executable(default_path, src_path); - } + if(boost::filesystem::is_directory(src_path, ec)) + executable = CMake(src_path).get_executable(default_path, src_path); } return executable; } diff --git a/tests/cmake_build_test.cpp b/tests/cmake_build_test.cpp index 1ae9066..0c5f0b9 100644 --- a/tests/cmake_build_test.cpp +++ b/tests/cmake_build_test.cpp @@ -5,10 +5,369 @@ #include #include -#include -using namespace std; - int main() { + { + bool called = false; + std::map> variables; + CMake::parse_file("", variables, [&called](CMake::Function && /*function*/) { + called = true; + }); + g_assert(!called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("project(", variables, [&called](CMake::Function && /*function*/) { + called = true; + }); + g_assert(!called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("project(test", variables, [&called](CMake::Function && /*function*/) { + called = true; + }); + g_assert(!called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("project(test)", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "project"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == "test"); + }); + g_assert(variables.size() == 2); + auto it = variables.begin(); + g_assert(it->first == "CMAKE_PROJECT_NAME"); + g_assert(it->second == std::list{"test"}); + ++it; + g_assert(it->first == "PROJECT_NAME"); + g_assert(it->second == std::list{"test"}); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("project(\"test\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "project"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == "test"); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("project(\"te\\\"st\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "project"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == "te\"st"); + }); + g_assert(called); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST testing)\nadd_executable(${TEST} test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "testing"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(${})", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"${}\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test($TEST)", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == "$TEST"); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(${TEST})", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"$TEST\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == "$TEST"); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"${TEST}\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 1); + g_assert(function.parameters.front() == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(${TEST} ${TEST})", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"${TEST}\" \"${TEST}\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(${TEST} \"${TEST}\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"${TEST}\" ${TEST})", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test(\"\" \"\")", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test( \"\" \"\" )", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + bool called = false; + std::map> variables; + CMake::parse_file("test\n(\n\"\"\n\"\"\n)", variables, [&called](CMake::Function &&function) { + called = true; + g_assert(function.name == "test"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == ""); + g_assert(*(++it) == ""); + }); + g_assert(called); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST testing)\nadd_executable(test${TEST}test test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "testtestingtest"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST testing)\nadd_executable(\"${TEST}\" test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "testing"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST testing)\nadd_executable(\"test${TEST}test\" test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "testtestingtest"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST 1 2 3)\nadd_executable(\"${TEST}\" test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "1;2;3"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST 1 2 3)\nadd_executable(\"aaa${TEST}bbb\" test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 2); + auto it = function.parameters.begin(); + g_assert(*it == "aaa1;2;3bbb"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST 1 2 3)\nadd_executable(${TEST} test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 4); + auto it = function.parameters.begin(); + g_assert(*it == "1"); + g_assert(*(++it) == "2"); + g_assert(*(++it) == "3"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + { + int called = 0; + std::map> variables; + CMake::parse_file("set(TEST 1 2 3)\nadd_executable(aaa${TEST}bbb test.cpp)", variables, [&called](CMake::Function &&function) { + called++; + if(called == 1) + g_assert(function.name == "set"); + else { + g_assert(function.name == "add_executable"); + g_assert(function.parameters.size() == 4); + auto it = function.parameters.begin(); + g_assert(*it == "aaa1"); + g_assert(*(++it) == "2"); + g_assert(*(++it) == "3bbb"); + g_assert(*(++it) == "test.cpp"); + } + }); + g_assert(called == 2); + } + auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH); { auto project_path = boost::filesystem::canonical(tests_path / ".."); @@ -33,9 +392,6 @@ int main() { g_assert(cmake.project_path == project_path); - auto functions_parameters = cmake.get_functions_parameters("project"); - g_assert(functions_parameters.at(0).second.at(0) == "juci"); - g_assert(cmake.get_executable(project_path / "build", tests_path).parent_path() == project_path / "build" / "tests"); g_assert(cmake.get_executable(project_path / "build", tests_path / "cmake_build_test.cpp") == project_path / "build" / "tests" / "cmake_build_test"); g_assert(cmake.get_executable(project_path / "build", tests_path / "non_existing_file.cpp").parent_path() == project_path / "build" / "tests");