mirror of
https://github.com/encounter/buildcache.git
synced 2026-07-09 18:18:50 -07:00
Merge branch 'feature/improve_configuration'
This commit is contained in:
@@ -119,15 +119,43 @@ $ BUILDCACHE_DEBUG=2 buildcache g++ -c -O2 hello.cpp -o hello.o
|
||||
|
||||
## Configuration options
|
||||
|
||||
The following environment variables control the behavior of BuildCache:
|
||||
BuildCache can be configured via environment variables and a per-cache JSON
|
||||
configuration file. The optinal configuration file is located in the cache
|
||||
root directory, and is called `config.json` (e.g.
|
||||
`$HOME/.buildcache/config.json`).
|
||||
|
||||
| Option | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `BUILDCACHE_DIR` | The cache root directory | `$HOME/.buildcache` |
|
||||
| `BUILDCACHE_PREFIX` | Prefix command for cache misses | None |
|
||||
| `BUILDCACHE_LUA_PATH` | Path(s) to Lua wrappers | None |
|
||||
| `BUILDCACHE_DEBUG` | Debug level | None |
|
||||
| `BUILDCACHE_PERF` | Enable performance logging | Disabled |
|
||||
The following options control the behavior of BuildCache:
|
||||
|
||||
| Env | JSON | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `BUILDCACHE_DIR` | - | The cache root directory | `$HOME/.buildcache` |
|
||||
| `BUILDCACHE_PREFIX` | `prefix` | Prefix command for cache misses | None |
|
||||
| `BUILDCACHE_LUA_PATH` | `lua_paths` | Extra path(s) to Lua wrappers | None |
|
||||
| `BUILDCACHE_DEBUG` | `debug` | Debug level | None |
|
||||
| `BUILDCACHE_MAX_CACHE_SIZE` | `max_cache_size` | Cache size limit in bytes | 5368709120 |
|
||||
| `BUILDCACHE_HARD_LINKS` | `hard_links` | Allow the use of hard links when caching | true |
|
||||
| `BUILDCACHE_PERF` | `perf` | Enable performance logging | false |
|
||||
| `BUILDCACHE_DISABLE` | `disable` | Disable caching (bypass BuildCache) | false |
|
||||
|
||||
An example configuration file:
|
||||
|
||||
```json
|
||||
{
|
||||
"max_cache_size": 10000000000,
|
||||
"prefix": "icecc",
|
||||
"debug": 3,
|
||||
"lua_paths": [
|
||||
"/home/myname/buildcache-lua",
|
||||
"/opt/buildcache-lua"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
To see the configuration options that are in effect, run:
|
||||
|
||||
```bash
|
||||
$ buildcache -s
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
|
||||
+3
-1
@@ -34,6 +34,8 @@ add_subdirectory(third_party)
|
||||
add_executable(buildcache
|
||||
cache.cpp
|
||||
cache.hpp
|
||||
configuration.cpp
|
||||
configuration.hpp
|
||||
debug_utils.cpp
|
||||
debug_utils.hpp
|
||||
file_utils.cpp
|
||||
@@ -61,7 +63,7 @@ add_executable(buildcache
|
||||
unicode_utils.cpp
|
||||
unicode_utils.hpp
|
||||
)
|
||||
target_link_libraries(buildcache md4 lua)
|
||||
target_link_libraries(buildcache cjson md4 lua)
|
||||
|
||||
if(WIN32 OR MINGW)
|
||||
target_link_libraries(buildcache userenv)
|
||||
|
||||
+29
-62
@@ -46,6 +46,7 @@
|
||||
|
||||
#include "cache.hpp"
|
||||
|
||||
#include "configuration.hpp"
|
||||
#include "debug_utils.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include "serializer_utils.hpp"
|
||||
@@ -53,45 +54,19 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace bcache {
|
||||
namespace {
|
||||
const int64_t DEFAULT_MAX_CACHE_SIZE_IN_BYTES = 5368709120L; // 5 GB
|
||||
|
||||
const std::string ROOT_FOLDER_NAME = ".buildcache";
|
||||
const std::string TEMP_FOLDER_NAME = "tmp";
|
||||
const std::string CACHE_FILES_FOLDER_NAME = "c";
|
||||
|
||||
const std::string CONFIGURATION_FILE_NAME = "buildcache.conf";
|
||||
const std::string CACHE_ENTRY_FILE_NAME = ".entry";
|
||||
|
||||
// The version of the entry file serialization data format.
|
||||
const int32_t ENTRY_DATA_FORMAT_VERSION = 1;
|
||||
|
||||
std::string find_root_folder() {
|
||||
// Is the environment variable BUILDCACHE_DIR defined?
|
||||
{
|
||||
const auto* buildcache_dir_env = std::getenv("BUILDCACHE_DIR");
|
||||
if (buildcache_dir_env != nullptr) {
|
||||
return std::string(buildcache_dir_env);
|
||||
}
|
||||
}
|
||||
|
||||
// Use the user home directory if possible.
|
||||
{
|
||||
auto home = file::get_user_home_dir();
|
||||
if (!home.empty()) {
|
||||
// TODO(m): Should use ".cache/buildcache".
|
||||
return file::append_path(home, ROOT_FOLDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// We failed.
|
||||
throw std::runtime_error("Unable to determine a home directory for BuildCache.");
|
||||
}
|
||||
|
||||
bool is_cache_entry_dir_path(const std::string& path) {
|
||||
const auto entry_dir_name = file::get_file_part(path);
|
||||
const auto hash_prefix_dir_name = file::get_file_part(file::get_dir_part(path));
|
||||
@@ -179,22 +154,17 @@ cache_t::entry_t deserialize_entry(const std::string& data) {
|
||||
} // namespace
|
||||
|
||||
cache_t::cache_t() {
|
||||
// Find the cache root folder.
|
||||
m_root_folder = find_root_folder();
|
||||
|
||||
// Can we use the cache?
|
||||
if (!file::dir_exists(m_root_folder)) {
|
||||
file::create_dir(m_root_folder);
|
||||
if (!file::dir_exists(config::dir())) {
|
||||
file::create_dir(config::dir());
|
||||
}
|
||||
|
||||
load_config();
|
||||
}
|
||||
|
||||
cache_t::~cache_t() {
|
||||
}
|
||||
|
||||
const std::string cache_t::get_tmp_folder() const {
|
||||
const auto tmp_path = file::append_path(m_root_folder, TEMP_FOLDER_NAME);
|
||||
const auto tmp_path = file::append_path(config::dir(), TEMP_FOLDER_NAME);
|
||||
if (!file::dir_exists(tmp_path)) {
|
||||
file::create_dir(tmp_path);
|
||||
}
|
||||
@@ -202,7 +172,7 @@ const std::string cache_t::get_tmp_folder() const {
|
||||
}
|
||||
|
||||
const std::string cache_t::get_cache_files_folder() const {
|
||||
const auto cache_files_path = file::append_path(m_root_folder, CACHE_FILES_FOLDER_NAME);
|
||||
const auto cache_files_path = file::append_path(config::dir(), CACHE_FILES_FOLDER_NAME);
|
||||
if (!file::dir_exists(cache_files_path)) {
|
||||
file::create_dir(cache_files_path);
|
||||
}
|
||||
@@ -217,7 +187,7 @@ const std::string cache_t::hash_to_cache_entry_path(const hasher_t::hash_t& hash
|
||||
|
||||
void cache_t::clear() {
|
||||
// Remove all cached files.
|
||||
const auto cache_files_path = file::append_path(m_root_folder, CACHE_FILES_FOLDER_NAME);
|
||||
const auto cache_files_path = file::append_path(config::dir(), CACHE_FILES_FOLDER_NAME);
|
||||
try {
|
||||
file::remove_dir(cache_files_path, true);
|
||||
std::cout << "Cleared the cache.\n";
|
||||
@@ -228,26 +198,30 @@ void cache_t::clear() {
|
||||
|
||||
void cache_t::show_stats() {
|
||||
// Calculate the total cache size.
|
||||
const auto dirs = get_cache_entry_dirs(m_root_folder);
|
||||
const auto dirs = get_cache_entry_dirs(config::dir());
|
||||
int num_entries = 0;
|
||||
int64_t total_size = 0;
|
||||
for (const auto& dir : dirs) {
|
||||
num_entries++;
|
||||
total_size += dir.size();
|
||||
}
|
||||
const double total_size_mb = static_cast<double>(total_size) / (1024.0 * 1024.0);
|
||||
const double max_size_mb = static_cast<double>(m_max_size) / (1024.0 * 1024.0);
|
||||
const auto total_size_mib = static_cast<double>(total_size) / (1024.0 * 1024.0);
|
||||
const auto max_size_mib = static_cast<double>(config::max_cache_size()) / (1024.0 * 1024.0);
|
||||
const auto full_percentage = 100.0 * total_size_mib / max_size_mib;
|
||||
|
||||
std::cout << "cache directory: " << m_root_folder << "\n";
|
||||
// std::cout << "primary config: " << get_configuration_file_name() << "\n";
|
||||
std::cout << "entries in cache: " << num_entries << "\n";
|
||||
std::cout << "cache size: " << total_size_mb << " MB\n";
|
||||
std::cout << "max cache size: " << max_size_mb << " MB\n";
|
||||
|
||||
// TODO(m): Implement more stats.
|
||||
// Print stats.
|
||||
std::ios old_fmt(nullptr);
|
||||
old_fmt.copyfmt(std::cout);
|
||||
std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(1);
|
||||
std::cout << " Entries in cache: " << num_entries << "\n";
|
||||
std::cout << " Cache size: " << total_size_mib << " MiB (" << full_percentage
|
||||
<< "%)\n";
|
||||
std::cout.copyfmt(old_fmt);
|
||||
}
|
||||
|
||||
void cache_t::add(const hasher_t::hash_t& hash, const cache_t::entry_t& entry) {
|
||||
void cache_t::add(const hasher_t::hash_t& hash,
|
||||
const cache_t::entry_t& entry,
|
||||
const bool allow_hard_links) {
|
||||
// Create the required directories in the cache.
|
||||
const auto cache_entry_path = hash_to_cache_entry_path(hash);
|
||||
const auto cache_entry_parent_path = file::get_dir_part(cache_entry_path);
|
||||
@@ -261,7 +235,11 @@ void cache_t::add(const hasher_t::hash_t& hash, const cache_t::entry_t& entry) {
|
||||
// Copy the files into the cache.
|
||||
for (const auto& file : entry.files) {
|
||||
const auto target_path = file::append_path(cache_entry_path, file.first);
|
||||
file::link_or_copy(file.second, target_path);
|
||||
if (allow_hard_links) {
|
||||
file::link_or_copy(file.second, target_path);
|
||||
} else {
|
||||
file::copy(file.second, target_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a cache entry file.
|
||||
@@ -303,24 +281,13 @@ cache_t::entry_t cache_t::lookup(const hasher_t::hash_t& hash) {
|
||||
}
|
||||
}
|
||||
|
||||
void cache_t::load_config() {
|
||||
// Set default values.
|
||||
m_max_size = DEFAULT_MAX_CACHE_SIZE_IN_BYTES;
|
||||
|
||||
// TODO(m): Load settings from a config file!
|
||||
}
|
||||
|
||||
void cache_t::save_config() {
|
||||
// TODO(m): Save settings to a config file!
|
||||
}
|
||||
|
||||
void cache_t::perform_housekeeping() {
|
||||
const auto start_t = std::chrono::high_resolution_clock::now();
|
||||
|
||||
debug::log(debug::INFO) << "Performing housekeeping.";
|
||||
|
||||
// Get all the cache entry directories.
|
||||
auto dirs = get_cache_entry_dirs(m_root_folder);
|
||||
auto dirs = get_cache_entry_dirs(config::dir());
|
||||
|
||||
// Sort the entries according to their access time (newest first).
|
||||
std::sort(
|
||||
@@ -335,7 +302,7 @@ void cache_t::perform_housekeeping() {
|
||||
for (const auto& dir : dirs) {
|
||||
num_entries++;
|
||||
total_size += dir.size();
|
||||
if (total_size > m_max_size) {
|
||||
if (total_size > config::max_cache_size()) {
|
||||
try {
|
||||
debug::log(debug::DEBUG) << "Purging " << dir.path() << " (last accessed "
|
||||
<< dir.access_time() << ", " << dir.size() << " bytes)";
|
||||
|
||||
+2
-13
@@ -51,12 +51,6 @@ public:
|
||||
/// @brief De-initialzie the cache object.
|
||||
~cache_t();
|
||||
|
||||
/// @brief Get the root folder of the cache.
|
||||
/// @returns the path to the root folder.
|
||||
const std::string& root_folder() const {
|
||||
return m_root_folder;
|
||||
}
|
||||
|
||||
/// @brief Clear all entries in the cache.
|
||||
void clear();
|
||||
|
||||
@@ -66,7 +60,8 @@ public:
|
||||
/// @brief Adds a set of files to the cache
|
||||
/// @param hash The cache entry identifier.
|
||||
/// @param entry The cache entry data (files, stdout, etc).
|
||||
void add(const hasher_t::hash_t& hash, const entry_t& entry);
|
||||
/// @param allow_hard_links Whether or not to allow hard links to be used when caching files.
|
||||
void add(const hasher_t::hash_t& hash, const entry_t& entry, const bool allow_hard_links);
|
||||
|
||||
/// @brief Check if an entry exists in the cache.
|
||||
/// @returns A cache hit struct.
|
||||
@@ -82,13 +77,7 @@ private:
|
||||
const std::string get_tmp_folder() const;
|
||||
const std::string get_cache_files_folder() const;
|
||||
|
||||
void load_config();
|
||||
void save_config();
|
||||
|
||||
void perform_housekeeping();
|
||||
|
||||
std::string m_root_folder;
|
||||
int64_t m_max_size;
|
||||
};
|
||||
|
||||
} // namespace bcache
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Marcus Geelnard
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty. In no event will the
|
||||
// authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
// applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not claim that you wrote
|
||||
// the original software. If you use this software in a product, an acknowledgment in the
|
||||
// product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be misrepresented as
|
||||
// being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "configuration.hpp"
|
||||
|
||||
#include "file_utils.hpp"
|
||||
|
||||
#include <cjson/cJSON.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace bcache {
|
||||
namespace {
|
||||
// Various constants.
|
||||
const std::string ROOT_FOLDER_NAME = ".buildcache";
|
||||
const std::string CONFIGURATION_FILE_NAME = "config.json";
|
||||
const int64_t DEFAULT_MAX_CACHE_SIZE = 5368709120L; // 5 GB
|
||||
|
||||
// Configuration options.
|
||||
std::string s_dir;
|
||||
string_list_t s_lua_paths;
|
||||
std::string s_prefix;
|
||||
int64_t s_max_cache_size = DEFAULT_MAX_CACHE_SIZE;
|
||||
int32_t s_debug = -1;
|
||||
bool s_hard_links = true;
|
||||
bool s_perf = false;
|
||||
bool s_disable = false;
|
||||
|
||||
std::string to_lower(const std::string& str) {
|
||||
std::string str_lower(str.size(), ' ');
|
||||
std::transform(str.begin(), str.end(), str_lower.begin(), ::tolower);
|
||||
return str_lower;
|
||||
}
|
||||
|
||||
/// @brief A helper class for reading and parsing environment variables.
|
||||
class env_var_t {
|
||||
public:
|
||||
env_var_t(const std::string& name) : m_defined(false) {
|
||||
const auto* env_var = std::getenv(name.c_str());
|
||||
if (env_var != nullptr) {
|
||||
m_value = std::string(env_var);
|
||||
m_defined = true;
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return m_defined;
|
||||
}
|
||||
|
||||
const std::string& as_string() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
int64_t as_int64() const {
|
||||
return std::stoll(m_value);
|
||||
}
|
||||
|
||||
bool as_bool() const {
|
||||
const auto value_lower = to_lower(m_value);
|
||||
return m_defined && (!m_value.empty()) && (value_lower != "false") && (value_lower != "no") &&
|
||||
(value_lower != "off") && (value_lower != "0");
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
bool m_defined;
|
||||
};
|
||||
|
||||
std::string get_dir() {
|
||||
// Is the environment variable BUILDCACHE_DIR defined?
|
||||
{
|
||||
const env_var_t dir_env("BUILDCACHE_DIR");
|
||||
if (dir_env) {
|
||||
return dir_env.as_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Use the user home directory if possible.
|
||||
{
|
||||
auto home = file::get_user_home_dir();
|
||||
if (!home.empty()) {
|
||||
// TODO(m): Should use ".cache/buildcache".
|
||||
return file::append_path(home, ROOT_FOLDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// We failed.
|
||||
throw std::runtime_error("Unable to determine a home directory for BuildCache.");
|
||||
}
|
||||
|
||||
void load_from_file(const std::string& file_name) {
|
||||
// Load the configuration file.
|
||||
if (!file::file_exists(file_name)) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
const auto data = file::read(file_name);
|
||||
|
||||
// Parse the JSON data.
|
||||
auto* root = cJSON_Parse(data.data());
|
||||
if (root == nullptr) {
|
||||
std::ostringstream ss;
|
||||
ss << "Configuration file JSON parse error before:\n";
|
||||
const auto* json_error = cJSON_GetErrorPtr();
|
||||
if (json_error != nullptr) {
|
||||
ss << json_error;
|
||||
} else {
|
||||
ss << "(N/A)";
|
||||
}
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
// Get "lua_paths".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "lua_paths");
|
||||
cJSON* child_node;
|
||||
cJSON_ArrayForEach(child_node, node) {
|
||||
const auto str = std::string(child_node->valuestring);
|
||||
s_lua_paths += str;
|
||||
}
|
||||
}
|
||||
|
||||
// Get "prefix".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "prefix");
|
||||
if (cJSON_IsString(node) && node->valuestring != nullptr) {
|
||||
s_prefix = std::string(node->valuestring);
|
||||
}
|
||||
}
|
||||
|
||||
// Get "max_cache_size".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "max_cache_size");
|
||||
if (cJSON_IsNumber(node)) {
|
||||
s_max_cache_size = static_cast<int64_t>(node->valuedouble);
|
||||
}
|
||||
}
|
||||
|
||||
// Get "debug".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "debug");
|
||||
if (cJSON_IsNumber(node)) {
|
||||
s_debug = static_cast<int32_t>(node->valueint);
|
||||
}
|
||||
}
|
||||
|
||||
// Get "hard_links".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "hard_links");
|
||||
if (cJSON_IsBool(node)) {
|
||||
s_hard_links = cJSON_IsTrue(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Get "perf".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "perf");
|
||||
if (cJSON_IsBool(node)) {
|
||||
s_perf = cJSON_IsTrue(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Get "disable".
|
||||
{
|
||||
const auto* node = cJSON_GetObjectItemCaseSensitive(root, "disable");
|
||||
if (cJSON_IsBool(node)) {
|
||||
s_disable = cJSON_IsTrue(node);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace config {
|
||||
void init() {
|
||||
// Guard: Only initialize once.
|
||||
static bool s_initialized = false;
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_initialized = true;
|
||||
|
||||
// TODO(m): If we get an exception during get_dir() or load_from_file() for instance, those error
|
||||
// messages will never be printed, *even* when BUILDCACHE_DEBUG=1 is passed in the environment.
|
||||
// We need to untie this catch-22.
|
||||
try {
|
||||
// Get the BuildCache home directory.
|
||||
s_dir = get_dir();
|
||||
|
||||
// Get the Lua paths from the environment.
|
||||
// Note: We need do this before loading the configuration file, in order for the environment to
|
||||
// have priority over the JSON file.
|
||||
{
|
||||
const env_var_t lua_path_env("BUILDCACHE_LUA_PATH");
|
||||
if (lua_path_env) {
|
||||
#ifdef _WIN32
|
||||
s_lua_paths += string_list_t(lua_path_env.as_string(), ";");
|
||||
#else
|
||||
s_lua_paths += string_list_t(lua_path_env.as_string(), ":");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Load any paramaters from the user configuration file.
|
||||
// Note: We do this before reading the configuration from the environment, so that the
|
||||
// environment overrides the configuration file.
|
||||
const auto config_file = file::append_path(s_dir, CONFIGURATION_FILE_NAME);
|
||||
load_from_file(config_file);
|
||||
|
||||
// We also look for Lua files in the cache root dir (e.g. ${BUILDCACHE_DIR}/lua).
|
||||
// Note: We need do this after loading the configuration file, to give the default Lua path the
|
||||
// lowest priority.
|
||||
s_lua_paths += file::append_path(s_dir, "lua");
|
||||
|
||||
// Get the command prefix from the environment.
|
||||
{
|
||||
const env_var_t prefix_env("BUILDCACHE_PREFIX");
|
||||
if (prefix_env) {
|
||||
s_prefix = prefix_env.as_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the max cache size from the environment.
|
||||
{
|
||||
const env_var_t max_cache_size_env("BUILDCACHE_MAX_CACHE_SIZE");
|
||||
if (max_cache_size_env) {
|
||||
try {
|
||||
s_max_cache_size = max_cache_size_env.as_int64();
|
||||
} catch (...) {
|
||||
// Ignore...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the debug level from the environment.
|
||||
{
|
||||
const env_var_t debug_env("BUILDCACHE_DEBUG");
|
||||
if (debug_env) {
|
||||
try {
|
||||
s_debug = static_cast<int32_t>(debug_env.as_int64());
|
||||
} catch (...) {
|
||||
// Ignore...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the hard_links flag from the environment.
|
||||
{
|
||||
const env_var_t hard_links_env("BUILDCACHE_HARD_LINKS");
|
||||
if (hard_links_env) {
|
||||
s_hard_links = hard_links_env.as_bool();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the perf flag from the environment.
|
||||
{
|
||||
const env_var_t perf_env("BUILDCACHE_PERF");
|
||||
if (perf_env) {
|
||||
s_perf = perf_env.as_bool();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the disabled flag from the environment.
|
||||
{
|
||||
const env_var_t disable_env("BUILDCACHE_DISABLE");
|
||||
if (disable_env) {
|
||||
s_disable = disable_env.as_bool();
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// If we could not initialize the configuration, we can't proceed. We need to disable the cache.
|
||||
s_disable = true;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& dir() {
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
const string_list_t& lua_paths() {
|
||||
return s_lua_paths;
|
||||
}
|
||||
|
||||
const std::string& prefix() {
|
||||
return s_prefix;
|
||||
}
|
||||
|
||||
int64_t max_cache_size() {
|
||||
return s_max_cache_size;
|
||||
}
|
||||
|
||||
int32_t debug() {
|
||||
return s_debug;
|
||||
}
|
||||
|
||||
bool hard_links() {
|
||||
return s_hard_links;
|
||||
}
|
||||
|
||||
bool perf() {
|
||||
return s_perf;
|
||||
}
|
||||
|
||||
bool disable() {
|
||||
return s_disable;
|
||||
}
|
||||
} // namespace config
|
||||
} // namespace bcache
|
||||
@@ -0,0 +1,61 @@
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Marcus Geelnard
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied warranty. In no event will the
|
||||
// authors be held liable for any damages arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose, including commercial
|
||||
// applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not claim that you wrote
|
||||
// the original software. If you use this software in a product, an acknowledgment in the
|
||||
// product documentation would be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be misrepresented as
|
||||
// being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifndef BUILDCACHE_CONFIGURATION_HPP_
|
||||
#define BUILDCACHE_CONFIGURATION_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "string_list.hpp"
|
||||
|
||||
namespace bcache {
|
||||
namespace config {
|
||||
/// @brief Initialize the configuration based on environment variables etc.
|
||||
void init();
|
||||
|
||||
/// @returns the BuildCache home directory.
|
||||
const std::string& dir();
|
||||
|
||||
/// @returns the Lua search paths.
|
||||
const string_list_t& lua_paths();
|
||||
|
||||
/// @returns the compiler exectution prefix command.
|
||||
const std::string& prefix();
|
||||
|
||||
/// @returns the maximum cache size (in bytes).
|
||||
int64_t max_cache_size();
|
||||
|
||||
/// @returns the debug level (-1 for no debugging).
|
||||
int32_t debug();
|
||||
|
||||
/// @returns true if BuildCache should use hard links when possible.
|
||||
bool hard_links();
|
||||
|
||||
/// @returns true if performance profiling output is enabled.
|
||||
bool perf();
|
||||
|
||||
/// @returns true if BuildCache is disabled.
|
||||
bool disable();
|
||||
|
||||
} // namespace config
|
||||
} // namespace bcache
|
||||
|
||||
#endif // BUILDCACHE_CONFIGURATION_HPP_
|
||||
+6
-24
@@ -19,8 +19,8 @@
|
||||
|
||||
#include "debug_utils.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include "configuration.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
namespace bcache {
|
||||
namespace debug {
|
||||
namespace {
|
||||
std::atomic_int s_log_level(-1);
|
||||
|
||||
std::string get_level_string(const log_level_t level) {
|
||||
switch (level) {
|
||||
case DEBUG:
|
||||
@@ -61,27 +59,11 @@ std::string get_level_string(const log_level_t level) {
|
||||
}
|
||||
|
||||
log_level_t get_log_level() {
|
||||
int log_level = s_log_level;
|
||||
int log_level = config::debug();
|
||||
|
||||
// The first time get_log_level() is called, s_log_level is undefined (negative).
|
||||
if (log_level < 0) {
|
||||
// Get the log level from the environment variable BUILDCACHE_DEBUG.
|
||||
const auto* log_level_env = std::getenv("BUILDCACHE_DEBUG");
|
||||
if (log_level_env != nullptr) {
|
||||
try {
|
||||
log_level = std::stoi(std::string(log_level_env));
|
||||
} catch (...) {
|
||||
}
|
||||
if ((log_level < static_cast<int>(DEBUG)) || (log_level > static_cast<int>(FATAL))) {
|
||||
log_level = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not get a valid log level, fall back to NONE (higher than the highest level).
|
||||
if (log_level < 0) {
|
||||
log_level = static_cast<int>(NONE);
|
||||
}
|
||||
s_log_level = log_level;
|
||||
// If we did not get a valid log level, fall back to NONE (higher than the highest level).
|
||||
if ((log_level < static_cast<int>(DEBUG)) || (log_level > static_cast<int>(FATAL))) {
|
||||
log_level = static_cast<int>(NONE);
|
||||
}
|
||||
|
||||
return static_cast<log_level_t>(log_level);
|
||||
|
||||
+95
-72
@@ -18,6 +18,7 @@
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "cache.hpp"
|
||||
#include "configuration.hpp"
|
||||
#include "debug_utils.hpp"
|
||||
#include "gcc_wrapper.hpp"
|
||||
#include "ghs_wrapper.hpp"
|
||||
@@ -38,27 +39,6 @@ namespace {
|
||||
// The name of the BuildCache executable (excluding the file extension).
|
||||
const std::string BUILDCACHE_EXE_NAME = "buildcache";
|
||||
|
||||
bcache::string_list_t get_lua_paths(const bcache::cache_t& cache) {
|
||||
bcache::string_list_t paths;
|
||||
|
||||
// The BUILDCACHE_LUA_PATH env variable can contain a colon-separated list of paths.
|
||||
{
|
||||
const auto* lua_path_env = std::getenv("BUILDCACHE_LUA_PATH");
|
||||
if (lua_path_env != nullptr) {
|
||||
#ifdef _WIN32
|
||||
paths += bcache::string_list_t(std::string(lua_path_env), ";");
|
||||
#else
|
||||
paths += bcache::string_list_t(std::string(lua_path_env), ":");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// We also look for Lua files in the cache root dir (e.g. ${BUILDCACHE_DIR}/lua).
|
||||
paths += bcache::file::append_path(cache.root_folder(), "lua");
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool is_lua_script(const std::string& script_path) {
|
||||
return (bcache::lower_case(bcache::file::get_extension(script_path)) == ".lua");
|
||||
}
|
||||
@@ -71,7 +51,7 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
|
||||
// Try Lua wrappers first (so you can override internal wrappers).
|
||||
// Iterate over the existing Lua paths.
|
||||
for (const auto& lua_root_dir : get_lua_paths(cache)) {
|
||||
for (const auto& lua_root_dir : bcache::config::lua_paths()) {
|
||||
if (bcache::file::dir_exists(lua_root_dir)) {
|
||||
// Find all .lua files in the given directory.
|
||||
const auto lua_files = bcache::file::walk_directory(lua_root_dir);
|
||||
@@ -81,13 +61,12 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
// Check if the given wrapper can handle this command (first match wins).
|
||||
wrapper.reset(new bcache::lua_wrapper_t(args, cache, script_path));
|
||||
if (wrapper->can_handle_command()) {
|
||||
bcache::debug::log(bcache::debug::DEBUG)
|
||||
<< "Found matching Lua wrapper for " << true_exe_path << ": " << script_path;
|
||||
bcache::debug::log(bcache::debug::DEBUG) << "Found matching Lua wrapper for "
|
||||
<< true_exe_path << ": " << script_path;
|
||||
break;
|
||||
} else {
|
||||
wrapper = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +111,33 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
int return_code = 0;
|
||||
try {
|
||||
bcache::cache_t cache;
|
||||
|
||||
// Print the cache stats.
|
||||
std::cout << "Cache status:\n";
|
||||
cache.show_stats();
|
||||
|
||||
{
|
||||
// Print the configuration.
|
||||
#ifdef _WIN32
|
||||
const std::string PATH_SEP = ";";
|
||||
#else
|
||||
const std::string PATH_SEP = ":";
|
||||
#endif
|
||||
|
||||
std::cout << "\nConfiguration:\n";
|
||||
std::cout << " BUILDCACHE_DIR: " << bcache::config::dir() << "\n";
|
||||
std::cout << " BUILDCACHE_LUA_PATH: "
|
||||
<< bcache::config::lua_paths().join(PATH_SEP, false) << "\n";
|
||||
std::cout << " BUILDCACHE_PREFIX: " << bcache::config::prefix() << "\n";
|
||||
std::cout << " BUILDCACHE_MAX_CACHE_SIZE: " << bcache::config::max_cache_size() << "\n";
|
||||
std::cout << " BUILDCACHE_DEBUG: " << bcache::config::debug() << "\n";
|
||||
std::cout << " BUILDCACHE_HARD_LINKS: "
|
||||
<< (bcache::config::hard_links() ? "true" : "false") << "\n";
|
||||
std::cout << " BUILDCACHE_PERF: " << (bcache::config::perf() ? "true" : "false")
|
||||
<< "\n";
|
||||
std::cout << " BUILDCACHE_DISABLE: " << (bcache::config::disable() ? "true" : "false")
|
||||
<< "\n";
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "*** Unexpected error: " << e.what() << "\n";
|
||||
return_code = 1;
|
||||
@@ -144,7 +149,7 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
}
|
||||
|
||||
[[noreturn]] void print_version_and_exit() {
|
||||
std::cout << "BuildCache version 0.2-dev\n";
|
||||
std::cout << "BuildCache version 0.3-dev\n";
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
@@ -175,52 +180,59 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
throw std::runtime_error("Missing arguments.");
|
||||
}
|
||||
|
||||
// Find the true path to the executable file. This affects things like if we can match the
|
||||
// compiler name or not, and what version string we get. We also want to avoid incorrectly
|
||||
// identifying other compiler accelerators (e.g. ccache) as actual compilers.
|
||||
// TODO(m): This call may throw an excepption, which currently means that we will not even try
|
||||
// to run the original command. At the same time this is a protection against endless symlink
|
||||
// recursion. Figure something out!
|
||||
PERF_START(FIND_EXECUTABLE);
|
||||
const auto true_exe_path = bcache::file::find_executable(args[0], BUILDCACHE_EXE_NAME);
|
||||
PERF_STOP(FIND_EXECUTABLE);
|
||||
|
||||
// Replace the command with the true exe path. Most of the following operations rely on having
|
||||
// a correct executable path. Also, this is important to avoid recursions when we are invoked
|
||||
// from a symlink, for instance.
|
||||
args[0] = true_exe_path;
|
||||
|
||||
try {
|
||||
return_code = 1;
|
||||
|
||||
// Initialize a cache object.
|
||||
bcache::cache_t cache;
|
||||
|
||||
// Select a matching compiler wrapper.
|
||||
PERF_START(FIND_WRAPPER);
|
||||
auto wrapper = find_suitable_wrapper(args, cache);
|
||||
PERF_STOP(FIND_WRAPPER);
|
||||
|
||||
// Run the wrapper, if any.
|
||||
if (wrapper) {
|
||||
was_wrapped = wrapper->handle_command(return_code);
|
||||
} else {
|
||||
bcache::debug::log(bcache::debug::INFO) << "No suitable wrapper for " << true_exe_path;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "Unexpected error: " << e.what();
|
||||
return_code = 1;
|
||||
} catch (...) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "Unexpected error.";
|
||||
return_code = 1;
|
||||
}
|
||||
|
||||
// Fall back to running the command as is.
|
||||
if (!was_wrapped) {
|
||||
PERF_START(RUN_FOR_FALLBACK);
|
||||
auto result = bcache::sys::run_with_prefix(args, false);
|
||||
PERF_STOP(RUN_FOR_FALLBACK);
|
||||
// Is the caching mechanism disabled?
|
||||
if (bcache::config::disable()) {
|
||||
// Bypass all the cache logic and call the intended command directly.
|
||||
auto result = bcache::sys::run(args, false);
|
||||
return_code = result.return_code;
|
||||
} else {
|
||||
// Find the true path to the executable file. This affects things like if we can match the
|
||||
// compiler name or not, and what version string we get. We also want to avoid incorrectly
|
||||
// identifying other compiler accelerators (e.g. ccache) as actual compilers.
|
||||
// TODO(m): This call may throw an exception, which currently means that we will not even try
|
||||
// to run the original command. At the same time this is a protection against endless symlink
|
||||
// recursion. Figure something out!
|
||||
PERF_START(FIND_EXECUTABLE);
|
||||
const auto true_exe_path = bcache::file::find_executable(args[0], BUILDCACHE_EXE_NAME);
|
||||
PERF_STOP(FIND_EXECUTABLE);
|
||||
|
||||
// Replace the command with the true exe path. Most of the following operations rely on having
|
||||
// a correct executable path. Also, this is important to avoid recursions when we are invoked
|
||||
// from a symlink, for instance.
|
||||
args[0] = true_exe_path;
|
||||
|
||||
try {
|
||||
return_code = 1;
|
||||
|
||||
// Initialize a cache object.
|
||||
bcache::cache_t cache;
|
||||
|
||||
// Select a matching compiler wrapper.
|
||||
PERF_START(FIND_WRAPPER);
|
||||
auto wrapper = find_suitable_wrapper(args, cache);
|
||||
PERF_STOP(FIND_WRAPPER);
|
||||
|
||||
// Run the wrapper, if any.
|
||||
if (wrapper) {
|
||||
was_wrapped = wrapper->handle_command(return_code);
|
||||
} else {
|
||||
bcache::debug::log(bcache::debug::INFO) << "No suitable wrapper for " << true_exe_path;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "Unexpected error: " << e.what();
|
||||
return_code = 1;
|
||||
} catch (...) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "Unexpected error.";
|
||||
return_code = 1;
|
||||
}
|
||||
|
||||
// Fall back to running the command as is.
|
||||
if (!was_wrapped) {
|
||||
PERF_START(RUN_FOR_FALLBACK);
|
||||
auto result = bcache::sys::run_with_prefix(args, false);
|
||||
PERF_STOP(RUN_FOR_FALLBACK);
|
||||
return_code = result.return_code;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
bcache::debug::log(bcache::debug::FATAL) << "Unexpected error: " << e.what();
|
||||
@@ -231,7 +243,9 @@ std::unique_ptr<bcache::program_wrapper_t> find_suitable_wrapper(const bcache::s
|
||||
}
|
||||
|
||||
// Report performance timings.
|
||||
bcache::perf::report();
|
||||
if (!bcache::config::disable()) {
|
||||
bcache::perf::report();
|
||||
}
|
||||
|
||||
std::exit(return_code);
|
||||
}
|
||||
@@ -260,6 +274,15 @@ void print_help(const char* program_name) {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
// Initialize the configuration.
|
||||
try {
|
||||
bcache::config::init();
|
||||
} catch (const std::exception& e) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "Warning: " << e.what();
|
||||
} catch (...) {
|
||||
bcache::debug::log(bcache::debug::ERROR) << "An exception occurred.";
|
||||
}
|
||||
|
||||
// Handle symlink invokation.
|
||||
if (bcache::file::get_file_part(std::string(argv[0]), false) != BUILDCACHE_EXE_NAME) {
|
||||
bcache::debug::log(bcache::debug::DEBUG) << "Invoked as symlink: " << argv[0];
|
||||
|
||||
+3
-10
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "perf_utils.hpp"
|
||||
|
||||
#include "configuration.hpp"
|
||||
#include "unicode_utils.hpp"
|
||||
|
||||
#include <chrono>
|
||||
@@ -36,15 +37,6 @@ int64_t get_time_in_us() {
|
||||
.count();
|
||||
return t;
|
||||
}
|
||||
|
||||
bool is_perf_enabled() {
|
||||
const auto* perf_env = std::getenv("BUILDCACHE_PERF");
|
||||
if (perf_env == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const auto perf = lower_case(std::string(perf_env));
|
||||
return (perf != "no") && (perf != "off") && (perf != "false");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int64_t start() {
|
||||
@@ -57,12 +49,13 @@ void stop(const int64_t start_time, const id_t id) {
|
||||
}
|
||||
|
||||
void report() {
|
||||
if (is_perf_enabled()) {
|
||||
if (config::perf()) {
|
||||
std::cerr << "Find exectuable: " << s_perf_log[ID_FIND_EXECUTABLE] << " us\n";
|
||||
std::cerr << "Find wrapper: " << s_perf_log[ID_FIND_WRAPPER] << " us\n";
|
||||
std::cerr << "Lua - Init: " << s_perf_log[ID_LUA_INIT] << " us\n";
|
||||
std::cerr << "Lua - Load script: " << s_perf_log[ID_LUA_LOAD_SCRIPT] << " us\n";
|
||||
std::cerr << "Lua - Run: " << s_perf_log[ID_LUA_RUN] << " us\n";
|
||||
std::cerr << "Get wrapper config: " << s_perf_log[ID_GET_WRAPPER_CONFIG] << " us\n";
|
||||
std::cerr << "Resolve args: " << s_perf_log[ID_RESOLVE_ARGS] << " us\n";
|
||||
std::cerr << "Preprocess: " << s_perf_log[ID_PREPROCESS] << " us\n";
|
||||
std::cerr << "Filter arguments: " << s_perf_log[ID_FILTER_ARGS] << " us\n";
|
||||
|
||||
+10
-9
@@ -30,15 +30,16 @@ enum id_t {
|
||||
ID_LUA_INIT = 2,
|
||||
ID_LUA_LOAD_SCRIPT = 3,
|
||||
ID_LUA_RUN = 4,
|
||||
ID_RESOLVE_ARGS = 5,
|
||||
ID_PREPROCESS = 6,
|
||||
ID_FILTER_ARGS = 7,
|
||||
ID_GET_PRG_ID = 8,
|
||||
ID_CACHE_LOOKUP = 9,
|
||||
ID_GET_BUILD_FILES = 10,
|
||||
ID_RUN_FOR_MISS = 11,
|
||||
ID_ADD_TO_CACHE = 12,
|
||||
ID_RUN_FOR_FALLBACK = 13,
|
||||
ID_GET_WRAPPER_CONFIG = 5,
|
||||
ID_RESOLVE_ARGS = 6,
|
||||
ID_PREPROCESS = 7,
|
||||
ID_FILTER_ARGS = 8,
|
||||
ID_GET_PRG_ID = 9,
|
||||
ID_CACHE_LOOKUP = 10,
|
||||
ID_GET_BUILD_FILES = 11,
|
||||
ID_RUN_FOR_MISS = 12,
|
||||
ID_ADD_TO_CACHE = 13,
|
||||
ID_RUN_FOR_FALLBACK = 14,
|
||||
NUM_PERF_IDS
|
||||
};
|
||||
|
||||
|
||||
+11
-2
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "program_wrapper.hpp"
|
||||
|
||||
#include "configuration.hpp"
|
||||
#include "debug_utils.hpp"
|
||||
#include "hasher.hpp"
|
||||
#include "perf_utils.hpp"
|
||||
@@ -66,6 +67,10 @@ bool program_wrapper_t::handle_command(int& return_code) {
|
||||
// Finalize the hash.
|
||||
const auto hash = hasher.final();
|
||||
|
||||
// Check if we can use hard links.
|
||||
// TODO(m): Add support for disabling hard links on a per wrapper basis.
|
||||
auto allow_hard_links = config::hard_links();
|
||||
|
||||
// Look up the entry in the cache.
|
||||
PERF_START(CACHE_LOOKUP);
|
||||
const auto cached_entry = m_cache.lookup(hash);
|
||||
@@ -84,7 +89,11 @@ bool program_wrapper_t::handle_command(int& return_code) {
|
||||
const auto& source_file = file.second;
|
||||
debug::log(debug::INFO) << "Cache hit (" << hash.as_string() << "): " << source_file
|
||||
<< " => " << target_file;
|
||||
file::link_or_copy(source_file, target_file);
|
||||
if (allow_hard_links) {
|
||||
file::link_or_copy(source_file, target_file);
|
||||
} else {
|
||||
file::copy(source_file, target_file);
|
||||
}
|
||||
}
|
||||
|
||||
// Return/print the cached program results.
|
||||
@@ -122,7 +131,7 @@ bool program_wrapper_t::handle_command(int& return_code) {
|
||||
new_entry.std_err = result.std_err;
|
||||
new_entry.return_code = result.return_code;
|
||||
PERF_START(ADD_TO_CACHE);
|
||||
m_cache.add(hash, new_entry);
|
||||
m_cache.add(hash, new_entry, allow_hard_links);
|
||||
PERF_STOP(ADD_TO_CACHE);
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "sys_utils.hpp"
|
||||
|
||||
#include "configuration.hpp"
|
||||
#include "debug_utils.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include "unicode_utils.hpp"
|
||||
@@ -266,13 +267,11 @@ run_result_t run_with_prefix(const string_list_t& args, const bool quiet) {
|
||||
// Prepend the argument list with a prefix, if any.
|
||||
bool is_icecc_prefix = false;
|
||||
string_list_t prefixed_args;
|
||||
const auto* prefix_env = std::getenv("BUILDCACHE_PREFIX");
|
||||
if (prefix_env != nullptr) {
|
||||
const auto prefix_str = std::string(prefix_env);
|
||||
prefixed_args += prefix_str;
|
||||
if (!config::prefix().empty()) {
|
||||
prefixed_args += config::prefix();
|
||||
|
||||
// Are we prefixed by ICECC?
|
||||
is_icecc_prefix = (file::get_file_part(prefix_str, false) == "icecc");
|
||||
is_icecc_prefix = (file::get_file_part(config::prefix(), false) == "icecc");
|
||||
}
|
||||
prefixed_args += args;
|
||||
|
||||
|
||||
Vendored
+5
@@ -22,6 +22,11 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCH
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w")
|
||||
endif()
|
||||
|
||||
add_library(cjson
|
||||
cjson/cJSON.c
|
||||
)
|
||||
target_include_directories(cjson INTERFACE .)
|
||||
|
||||
add_library(md4
|
||||
md4/md4.c
|
||||
)
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Vendored
+2932
File diff suppressed because it is too large
Load Diff
Vendored
+277
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef cJSON__h
|
||||
#define cJSON__h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 7
|
||||
#define CJSON_VERSION_PATCH 7
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
#define cJSON_Number (1 << 3)
|
||||
#define cJSON_String (1 << 4)
|
||||
#define cJSON_Array (1 << 5)
|
||||
#define cJSON_Object (1 << 6)
|
||||
#define cJSON_Raw (1 << 7) /* raw json */
|
||||
|
||||
#define cJSON_IsReference 256
|
||||
#define cJSON_StringIsConst 512
|
||||
|
||||
/* The cJSON structure: */
|
||||
typedef struct cJSON
|
||||
{
|
||||
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||
struct cJSON *next;
|
||||
struct cJSON *prev;
|
||||
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||
struct cJSON *child;
|
||||
|
||||
/* The type of the item, as above. */
|
||||
int type;
|
||||
|
||||
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||
char *valuestring;
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
|
||||
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||
char *string;
|
||||
} cJSON;
|
||||
|
||||
typedef struct cJSON_Hooks
|
||||
{
|
||||
void *(*malloc_fn)(size_t sz);
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type __stdcall
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
|
||||
#endif
|
||||
#else /* !WIN32 */
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check if the item is a string and return its valuestring */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
/* raw json */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
|
||||
/* Create a string where valuestring references a string so
|
||||
* it will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
/* Create an object/arrray that only references it's elements so
|
||||
* they will not be freed by cJSON_Delete */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Helper functions for creating and adding items to an object at the same time.
|
||||
* They return the added item or NULL on failure. */
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
|
||||
/* Macro for iterating over an array or object */
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user