Browse Source

Use CMake file API to find executables for source file

This is a lot more precise than the currently exisiting method and
correctly detects any modification to the executable file path
(e.g. setting CMAKE_RUNTIME_OUTPUT_DIRECTORY or the OUTPUT_NAME property).
merge-requests/413/head
doe300 4 years ago
parent
commit
6c4de943ec
  1. 77
      src/cmake.cpp
  2. 4
      src/cmake.hpp
  3. 4
      tests/CMakeLists.txt
  4. 27
      tests/cmake_file_api_test.cpp
  5. 8
      tests/cmake_file_api_test_files/CMakeLists.txt
  6. 4
      tests/cmake_file_api_test_files/main.cpp

77
src/cmake.cpp

@ -3,6 +3,7 @@
#include "config.hpp" #include "config.hpp"
#include "dialog.hpp" #include "dialog.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "json.hpp"
#include "terminal.hpp" #include "terminal.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -59,6 +60,8 @@ bool CMake::update_default_build(const boost::filesystem::path &default_build_pa
if(!force && boost::filesystem::exists(default_build_path / "compile_commands.json", ec)) if(!force && boost::filesystem::exists(default_build_path / "compile_commands.json", ec))
return true; return true;
set_file_api_queries(default_build_path);
auto compile_commands_path = default_build_path / "compile_commands.json"; auto compile_commands_path = default_build_path / "compile_commands.json";
bool canceled = false; bool canceled = false;
Dialog::Message message("Creating/updating default build", [&canceled] { Dialog::Message message("Creating/updating default build", [&canceled] {
@ -120,6 +123,8 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path,
if(!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt", ec)) if(!force && boost::filesystem::exists(debug_build_path / "CMakeCache.txt", ec))
return true; return true;
set_file_api_queries(debug_build_path);
bool canceled = false; bool canceled = false;
Dialog::Message message("Creating/updating debug build", [&canceled] { Dialog::Message message("Creating/updating debug build", [&canceled] {
canceled = true; canceled = true;
@ -145,6 +150,13 @@ bool CMake::update_debug_build(const boost::filesystem::path &debug_build_path,
} }
boost::filesystem::path CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) { boost::filesystem::path CMake::get_executable(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
// prefer the CMake file API (if available) which gives exact information about the targets a file belongs to
auto executables = get_executables(build_path, file_path);
if(!executables.empty()) {
return executables.front();
}
// CMake does not store in compile_commands.json if an object is part of an executable or not. // CMake does not store in compile_commands.json if an object is part of an executable or not.
// Therefore, executables are first attempted found in the cmake files. These executables // 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 // are then used to identify if a file in compile_commands.json is part of an executable or not
@ -349,3 +361,68 @@ void CMake::parse_file(const std::string &src, std::map<std::string, std::list<s
} }
} }
} }
void CMake::set_file_api_queries(const boost::filesystem::path &build_path) {
auto query_directory = build_path / ".cmake" / "api" / "v1" / "query" / "client-jucipp";
boost::system::error_code ec;
if(!boost::filesystem::exists(query_directory, ec)) {
boost::system::error_code ec;
boost::filesystem::create_directories(query_directory, ec);
if(ec) {
Terminal::get().print("\e[31mError\e[m: could not create " + filesystem::get_short_path(query_directory).string() + ": " + ec.message() + "\n", true);
return;
}
}
filesystem::write(query_directory / "query.json", R"({
"requests": [
{ "kind": "codemodel" , "version": 2 }
]
})");
}
std::vector<boost::filesystem::path> CMake::get_executables(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path) {
auto reply_directory = build_path / ".cmake" / "api" / "v1" / "reply";
boost::system::error_code ec;
if(!boost::filesystem::exists(reply_directory, ec)) {
// If the reply directory does not exist, either CMake was not run or the file API is not yet supported
return {};
}
// check all target-*.json files and filter for the given file path
std::vector<boost::filesystem::path> executables;
for(const auto &reply_file : boost::filesystem::directory_iterator(reply_directory)) {
if(reply_file.path().stem().string().find("target-") != 0) {
continue;
}
JSON json{reply_file.path()};
auto artifacts = json.array_optional("artifacts");
if(!artifacts || artifacts->empty()) {
continue;
}
auto relative_path = artifacts->front().string_optional("path");
if(!relative_path) {
continue;
}
auto type = json.string_optional("type");
if(!type || *type != "EXECUTABLE") {
continue;
}
auto sources = json.array_optional("sources");
if(!sources) {
continue;
}
for(const auto &source : *sources) {
auto source_path = project_path / source.string_or("path", "");
if(source_path == file_path) {
executables.emplace_back(build_path / *relative_path);
}
}
}
return executables;
}

4
src/cmake.hpp

@ -22,4 +22,8 @@ private:
std::list<std::string> parameters; std::list<std::string> parameters;
}; };
static void parse_file(const std::string &src, std::map<std::string, std::list<std::string>> &variables, std::function<void(Function &&)> &&on_function); static void parse_file(const std::string &src, std::map<std::string, std::list<std::string>> &variables, std::function<void(Function &&)> &&on_function);
// Cmake file API functions
void set_file_api_queries(const boost::filesystem::path &build_path);
std::vector<boost::filesystem::path> get_executables(const boost::filesystem::path &build_path, const boost::filesystem::path &file_path);
}; };

4
tests/CMakeLists.txt

@ -34,6 +34,10 @@ if(BUILD_TESTING)
target_link_libraries(cmake_build_test juci_shared) target_link_libraries(cmake_build_test juci_shared)
add_test(cmake_build_test cmake_build_test) add_test(cmake_build_test cmake_build_test)
add_executable(cmake_file_api_test cmake_file_api_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(cmake_file_api_test juci_shared)
add_test(cmake_file_api_test cmake_file_api_test)
add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>) add_executable(meson_build_test meson_build_test.cpp $<TARGET_OBJECTS:test_stubs>)
target_link_libraries(meson_build_test juci_shared) target_link_libraries(meson_build_test juci_shared)
add_test(meson_build_test meson_build_test) add_test(meson_build_test meson_build_test)

27
tests/cmake_file_api_test.cpp

@ -0,0 +1,27 @@
#include "cmake.hpp"
#include "config.hpp"
#include "process.hpp"
#include "project_build.hpp"
#include <boost/filesystem.hpp>
#include <glib.h>
#include <gtksourceviewmm.h>
int main() {
auto app = Gtk::Application::create();
Gsv::init();
Config::get().project.default_build_path = "./build";
Config::get().project.cmake.command = "cmake";
auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH);
auto cmake_test_files_path = boost::filesystem::canonical(tests_path / "cmake_file_api_test_files");
CMake cmake(cmake_test_files_path);
g_assert(cmake.project_path == cmake_test_files_path);
auto build = Project::Build::create(cmake_test_files_path);
g_assert(dynamic_cast<Project::CMakeBuild *>(build.get()));
g_assert(build->update_default(true));
g_assert(build->get_executable(cmake_test_files_path / "main.cpp") == cmake_test_files_path / "build" / "custom_build_folder" / "custom_executable");
}

8
tests/cmake_file_api_test_files/CMakeLists.txt

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.1)
project(test)
add_compile_options(-O0 -g)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/custom_build_folder)
add_executable(cmake_file_api_test_executable main.cpp)
set_target_properties(cmake_file_api_test_executable PROPERTIES OUTPUT_NAME "custom_executable")

4
tests/cmake_file_api_test_files/main.cpp

@ -0,0 +1,4 @@
int main() {
int an_int = 1;
an_int++;
}
Loading…
Cancel
Save