Skip to content

Commit

Permalink
[wimal] Add bear action to generate compile_commands.json
Browse files Browse the repository at this point in the history
  • Loading branch information
disigma committed Apr 20, 2022
1 parent b4dbcad commit a30dd47
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,6 @@ __pycache__/
### wimal
/cmake/.cache
/cmake/wimal

### clangd
/.cache
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ add_subdirectory(xar)
# Add uuid
add_subdirectory(uuid)

# Add jsoncpp
set(JSONCPP_WITH_TESTS OFF CACHE BOOL JSONCPP_WITH_TESTS)
set(JSONCPP_WITH_POST_BUILD_UNITTEST OFF CACHE BOOL JSONCPP_WITH_POST_BUILD_UNITTEST)
set(JSONCPP_WITH_WARNING_AS_ERROR OFF CACHE BOOL JSONCPP_WITH_WARNING_AS_ERROR)
set(JSONCPP_WITH_PKGCONFIG_SUPPORT OFF CACHE BOOL JSONCPP_WITH_PKGCONFIG_SUPPORT)
set(JSONCPP_WITH_CMAKE_PACKAGE OFF CACHE BOOL JSONCPP_WITH_CMAKE_PACKAGE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL BUILD_SHARED_LIBS)
add_subdirectory(jsoncpp)

# Add libuv
add_subdirectory(libuv)

# Disable terminfo
set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL LLVM_ENABLE_TERMINFO)

Expand Down
10 changes: 9 additions & 1 deletion wimal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ project(wimal)

set(CMAKE_CXX_STANDARD 11)

include_directories(.)
get_target_property(JSONCPP_INCLUDE_DIRECTORIES jsoncpp_static INCLUDE_DIRECTORIES)
get_target_property(LIBUV_INCLUDE_DIRECTORIES uv_a INCLUDE_DIRECTORIES)
include_directories(
.
${JSONCPP_INCLUDE_DIRECTORIES}
${LIBUV_INCLUDE_DIRECTORIES}
)

add_executable(
wimal
action.cpp
bear.cpp
cc.cpp
context.cpp
export.cpp
Expand All @@ -26,4 +33,5 @@ add_executable(
boost/filesystem/utf8_codecvt_facet.cpp
boost/filesystem/windows_file_codecvt.cpp
)
target_link_libraries(wimal jsoncpp_static uv_a pthread)
install(TARGETS wimal DESTINATION bin COMPONENT wimal)
4 changes: 3 additions & 1 deletion wimal/action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <iostream>

#include "bear.hpp"
#include "cc.hpp"
#include "context.hpp"
#include "install.hpp"
Expand All @@ -23,7 +24,8 @@ const std::unordered_map<std::string, std::shared_ptr<Action>> Action::actions{
{"strip", std::make_shared<Invoke>()},
{"readelf", std::make_shared<Invoke>()},
{"dsymutil", std::make_shared<Invoke>()},
{"install_name_tool", std::make_shared<Invoke>()}
{"install_name_tool", std::make_shared<Invoke>()},
{"bear", std::make_shared<Bear>()},
};

Action::Action() = default;
Expand Down
153 changes: 153 additions & 0 deletions wimal/bear.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "bear.hpp"

#include <uv.h>
#include <json/json.h>
#include <list>

#include "context.hpp"

namespace wimal {

struct BearCache {
int status{0};
std::list<std::string> commands{};
uv_udp_t udp{};
uv_process_t process{};
};

static inline void Serve(BearCache *cache) {
auto &udp = cache->udp;
udp.data = cache;
uv_udp_init(uv_default_loop(), &udp);
sockaddr_in address{};
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = htons(0);
if (uv_udp_bind(&udp, (sockaddr *) &address, 0)) {
return;
}
int length = sizeof(address);
if (uv_udp_getsockname(&udp, (sockaddr *) &address, &length)) {
return;
}
auto port = ntohs(address.sin_port);
setenv("WIMAL_BEAR_PORT", std::to_string(port).data(), 1);
static char kBuffer[65536];
uv_udp_recv_start(&udp, [](uv_handle_t *handle, size_t suggestedSize, uv_buf_t *buf) {
buf->base = kBuffer;
buf->len = sizeof(kBuffer);
}, [](uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, const sockaddr *, unsigned) {
if (nread <= 0) {
return;
}
auto cache = (BearCache *) handle->data;
cache->commands.emplace_back(std::string(buf->base, nread));
});
}

static inline void Spawn(BearCache *cache, const Context *context, const std::vector<std::string> &extraArgs) {
std::vector<char *> arguments;
arguments.reserve(context->args.size() + extraArgs.size() + 1);
if (context->args.empty()) {
return;
}
boost::filesystem::path command;
for (const auto &arg : context->args) {
if (command.empty()) {
if (arg.empty() || arg[0] == '-') {
continue;
}
command = boost::filesystem::path(arg);
}
arguments.emplace_back(const_cast<char *>(arg.data()));
}
if (command.empty()) {
return;
}
for (const auto &arg : extraArgs) {
arguments.emplace_back(const_cast<char *>(arg.data()));
}
arguments.emplace_back(nullptr);
uv_process_options_t options{};
memset(&options, 0, sizeof(options));
options.exit_cb = [](uv_process_t *handle, int64_t status, int) {
auto cache = (BearCache *) handle->data;
uv_udp_recv_stop(&cache->udp);
cache->status = (int) status;
uv_close((uv_handle_t *) &cache->udp, [](uv_handle_t *) {});
};
options.args = arguments.data();
options.file = command.c_str();
options.stdio_count = 3;
uv_stdio_container_t stdio[3];
stdio[0].flags = UV_INHERIT_FD;
stdio[0].data.fd = 0;
stdio[1].flags = UV_INHERIT_FD;
stdio[1].data.fd = 1;
stdio[2].flags = UV_INHERIT_FD;
stdio[2].data.fd = 2;
options.stdio = stdio;
cache->process.data = cache;
uv_spawn(uv_default_loop(), &cache->process, &options);
}

static inline void Save(BearCache *cache) {
constexpr const char *kCompileCommandsFile = "compile_commands.json";
std::list<Json::Value> commands;
std::unordered_map<std::string, std::list<Json::Value>::iterator> fileCommands;
// Read existing compile_commands.json
Json::Reader reader;
Json::Value allCommands(Json::arrayValue);
std::ifstream istream(kCompileCommandsFile);
if (istream) {
if (!reader.parse(istream, allCommands) || !allCommands.isArray()) {
allCommands = Json::Value(Json::arrayValue);
}
istream.close();
}
// Parse collected commands
for (auto &command : cache->commands) {
Json::Value object;
if (reader.parse(command, object) && object.isObject()) {
allCommands.append(std::move(object));
}
}
// Merge commands
for (auto &command : allCommands) {
auto &object = command;
auto &iFile = object["file"];
if (!iFile.isString()) {
continue;
}
auto file = iFile.asString();
auto entry = commands.emplace(commands.end(), object);
auto it = fileCommands.find(file);
if (it != fileCommands.end()) {
commands.erase(it->second);
it->second = entry;
} else {
fileCommands.emplace(file, entry);
}
}
allCommands.clear();
for (auto &command : commands) {
allCommands.append(std::move(command));
}
// Write compile_commands.json
std::ofstream stream(kCompileCommandsFile);
auto writer = Json::StreamWriterBuilder().newStreamWriter();
writer->write(allCommands, &stream);
stream.close();
}

void Bear::Run(const Context *context, std::vector<std::string> extraArgs) {
BearCache cache;
Serve(&cache);
Spawn(&cache, context, extraArgs);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
Save(&cache);
exit(cache.status);
}

}
14 changes: 14 additions & 0 deletions wimal/bear.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef WIMAL_BEAR_HPP
#define WIMAL_BEAR_HPP

#include "action.hpp"

namespace wimal {

class Bear : public Action {
void Run(const Context *context, std::vector<std::string> extraArgs) override;
};

}

#endif // WIMAL_BEAR_HPP
Loading

0 comments on commit a30dd47

Please sign in to comment.