#include <dds/toolchain.hpp> | #include <dds/toolchain.hpp> | ||||
#include <algorithm> | #include <algorithm> | ||||
#include <sstream> | |||||
#include <iomanip> | #include <iomanip> | ||||
#include <iostream> | #include <iostream> | ||||
#include <mutex> | #include <mutex> | ||||
#include <sstream> | |||||
#include <stdexcept> | #include <stdexcept> | ||||
#include <thread> | #include <thread> | ||||
std::unique_lock lk{mut}; | std::unique_lock lk{mut}; | ||||
std::vector<std::thread> threads; | std::vector<std::thread> threads; | ||||
int njobs = params.parallel_jobs; | |||||
int njobs = params.parallel_jobs; | |||||
if (njobs < 1) { | if (njobs < 1) { | ||||
njobs = std::thread::hardware_concurrency() + 2; | njobs = std::thread::hardware_concurrency() + 2; | ||||
} | } | ||||
std::generate_n(std::back_inserter(threads), njobs, [&] { | |||||
return std::thread(compile_one); | |||||
}); | |||||
std::generate_n(std::back_inserter(threads), njobs, [&] { return std::thread(compile_one); }); | |||||
spdlog::info("Parallel compile with {} threads", threads.size()); | spdlog::info("Parallel compile with {} threads", threads.size()); | ||||
lk.unlock(); | lk.unlock(); | ||||
for (auto& t : threads) { | for (auto& t : threads) { |
#ifndef DDS_BUILD_HPP_INCLUDED | #ifndef DDS_BUILD_HPP_INCLUDED | ||||
#define DDS_BUILD_HPP_INCLUDED | #define DDS_BUILD_HPP_INCLUDED | ||||
#include <dds/util.hpp> | |||||
#include <dds/manifest.hpp> | #include <dds/manifest.hpp> | ||||
#include <dds/util.hpp> | |||||
#include <optional> | #include <optional> | ||||
fs::path out_root; | fs::path out_root; | ||||
fs::path toolchain_file; | fs::path toolchain_file; | ||||
std::string export_name; | std::string export_name; | ||||
bool do_export = false; | |||||
bool build_tests = false; | |||||
bool do_export = false; | |||||
bool build_tests = false; | |||||
bool enable_warnings = false; | bool enable_warnings = false; | ||||
int parallel_jobs = 0; | |||||
int parallel_jobs = 0; | |||||
}; | }; | ||||
void build(const build_params&, const library_manifest& man); | void build(const build_params&, const library_manifest& man); |
#include <dds/build.hpp> | #include <dds/build.hpp> | ||||
#include <dds/lm_parse.hpp> | #include <dds/lm_parse.hpp> | ||||
#include <dds/util.hpp> | |||||
#include <dds/logging.hpp> | #include <dds/logging.hpp> | ||||
#include <dds/util.hpp> | |||||
#include <args.hxx> | #include <args.hxx> | ||||
"Enable compiler warnings", | "Enable compiler warnings", | ||||
{"warnings", 'W'}}; | {"warnings", 'W'}}; | ||||
args::ValueFlag<int> num_jobs{ cmd, | |||||
"jobs", | |||||
"Set the number of parallel jobs when compiling files", | |||||
{ "jobs", 'j' }, | |||||
0 }; | |||||
args::ValueFlag<int> num_jobs{cmd, | |||||
"jobs", | |||||
"Set the number of parallel jobs when compiling files", | |||||
{"jobs", 'j'}, | |||||
0}; | |||||
int run() { | int run() { | ||||
dds::build_params params; | dds::build_params params; | ||||
params.root = lib_dir.Get(); | |||||
params.out_root = out_dir.Get(); | |||||
params.toolchain_file = tc_filepath.Get(); | |||||
params.export_name = export_name.Get(); | |||||
params.do_export = export_.Get(); | |||||
params.build_tests = build_tests.Get(); | |||||
params.root = lib_dir.Get(); | |||||
params.out_root = out_dir.Get(); | |||||
params.toolchain_file = tc_filepath.Get(); | |||||
params.export_name = export_name.Get(); | |||||
params.do_export = export_.Get(); | |||||
params.build_tests = build_tests.Get(); | |||||
params.enable_warnings = enable_warnings.Get(); | params.enable_warnings = enable_warnings.Get(); | ||||
params.parallel_jobs = num_jobs.Get(); | params.parallel_jobs = num_jobs.Get(); | ||||
dds::library_manifest man; | dds::library_manifest man; | ||||
const auto man_filepath = params.root / "manifest.dds"; | |||||
const auto man_filepath = params.root / "manifest.dds"; | |||||
if (exists(man_filepath)) { | if (exists(man_filepath)) { | ||||
man = dds::library_manifest::load_from_file(man_filepath); | man = dds::library_manifest::load_from_file(man_filepath); | ||||
} | } |
#include <dds/util.hpp> | #include <dds/util.hpp> | ||||
#include <fstream> | |||||
#include <cctype> | #include <cctype> | ||||
#include <fstream> | |||||
namespace fs = std::filesystem; | namespace fs = std::filesystem; | ||||
#include <spdlog/sinks/stdout_color_sinks.h> | #include <spdlog/sinks/stdout_color_sinks.h> | ||||
#include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||
namespace dds { | |||||
inline auto get_logger() { return spdlog::stdout_color_mt("console"); } | |||||
} // namespace dds | |||||
#endif // DDS_LOGGING_HPP_INCLUDED | |||||
#endif // DDS_LOGGING_HPP_INCLUDED |
return res; | return res; | ||||
} | } | ||||
#endif // _WIN32 | |||||
#endif // _WIN32 |
auto cmd_str = concat_args(cmd); | auto cmd_str = concat_args(cmd); | ||||
::SECURITY_ATTRIBUTES security = {}; | ::SECURITY_ATTRIBUTES security = {}; | ||||
security.bInheritHandle = TRUE; | |||||
security.nLength = sizeof security; | |||||
security.lpSecurityDescriptor = nullptr; | |||||
security.bInheritHandle = TRUE; | |||||
security.nLength = sizeof security; | |||||
security.lpSecurityDescriptor = nullptr; | |||||
wil::unique_hfile reader; | wil::unique_hfile reader; | ||||
wil::unique_hfile writer; | wil::unique_hfile writer; | ||||
auto okay = ::CreatePipe(&reader, &writer, &security, 0); | |||||
auto okay = ::CreatePipe(&reader, &writer, &security, 0); | |||||
if (!okay) { | if (!okay) { | ||||
throw_system_error("Failed to create a stdio pipe"); | throw_system_error("Failed to create a stdio pipe"); | ||||
} | } | ||||
::STARTUPINFOA startup_info = {}; | ::STARTUPINFOA startup_info = {}; | ||||
::RtlSecureZeroMemory(&startup_info, sizeof startup_info); | ::RtlSecureZeroMemory(&startup_info, sizeof startup_info); | ||||
startup_info.hStdOutput = startup_info.hStdError = writer.get(); | startup_info.hStdOutput = startup_info.hStdError = writer.get(); | ||||
startup_info.dwFlags = STARTF_USESTDHANDLES; | |||||
startup_info.cb = sizeof startup_info; | |||||
startup_info.dwFlags = STARTF_USESTDHANDLES; | |||||
startup_info.cb = sizeof startup_info; | |||||
// DO IT! | |||||
okay = ::CreateProcessA(nullptr, // cmd[0].data(), | okay = ::CreateProcessA(nullptr, // cmd[0].data(), | ||||
cmd_str.data(), | cmd_str.data(), | ||||
nullptr, | nullptr, | ||||
std::string output; | std::string output; | ||||
while (true) { | while (true) { | ||||
const int buffer_size = 256; | const int buffer_size = 256; | ||||
char buffer[buffer_size]; | |||||
DWORD nread = 0; | |||||
okay = ::ReadFile(reader.get(), buffer, buffer_size, &nread, nullptr); | |||||
char buffer[buffer_size]; | |||||
DWORD nread = 0; | |||||
okay = ::ReadFile(reader.get(), buffer, buffer_size, &nread, nullptr); | |||||
if (!okay && ::GetLastError() != ERROR_BROKEN_PIPE) { | if (!okay && ::GetLastError() != ERROR_BROKEN_PIPE) { | ||||
throw_system_error("Failed while reading from the stdio pipe"); | throw_system_error("Failed while reading from the stdio pipe"); | ||||
} | } | ||||
::WaitForSingleObject(proc_info.hProcess, INFINITE); | ::WaitForSingleObject(proc_info.hProcess, INFINITE); | ||||
DWORD rc = 0; | DWORD rc = 0; | ||||
okay = ::GetExitCodeProcess(proc_info.hProcess, &rc); | |||||
okay = ::GetExitCodeProcess(proc_info.hProcess, &rc); | |||||
if (!okay || rc == STILL_ACTIVE) { | if (!okay || rc == STILL_ACTIVE) { | ||||
throw_system_error("Failed reading exit code of process"); | throw_system_error("Failed reading exit code of process"); | ||||
} | } | ||||
proc_result res; | proc_result res; | ||||
res.retc = rc; | |||||
res.retc = rc; | |||||
res.output = std::move(output); | res.output = std::move(output); | ||||
return res; | return res; | ||||
} | } |
vector<string> cmd; | vector<string> cmd; | ||||
for (auto& arg : _archive_template) { | for (auto& arg : _archive_template) { | ||||
if (arg == "<OBJECTS>") { | if (arg == "<OBJECTS>") { | ||||
std::transform(spec.input_files.begin(), spec.input_files.end(), std::back_inserter(cmd), [](auto&& p) { | |||||
return p.string(); | |||||
}); | |||||
std::transform(spec.input_files.begin(), | |||||
spec.input_files.end(), | |||||
std::back_inserter(cmd), | |||||
[](auto&& p) { return p.string(); }); | |||||
} else { | } else { | ||||
cmd.push_back(replace(arg, "<ARCHIVE>", spec.out_path.string())); | cmd.push_back(replace(arg, "<ARCHIVE>", spec.out_path.string())); | ||||
} | } |
struct compile_file_spec { | struct compile_file_spec { | ||||
fs::path source_path; | fs::path source_path; | ||||
fs::path out_path; | fs::path out_path; | ||||
std::vector<std::string> definitions = {}; | |||||
std::vector<fs::path> include_dirs = {}; | |||||
language lang = language::automatic; | |||||
std::vector<std::string> definitions = {}; | |||||
std::vector<fs::path> include_dirs = {}; | |||||
language lang = language::automatic; | |||||
bool enable_warnings = false; | bool enable_warnings = false; | ||||
}; | }; | ||||
return ret; | return ret; | ||||
} | } | ||||
std::string dds::slurp_file(const fs::path& path, std::error_code& ec) { | std::string dds::slurp_file(const fs::path& path, std::error_code& ec) { | ||||
auto file = dds::open(path, std::ios::in, ec); | auto file = dds::open(path, std::ios::in, ec); | ||||
if (ec) { | if (ec) { |
#ifndef DDS_UTIL_HPP_INCLUDED | #ifndef DDS_UTIL_HPP_INCLUDED | ||||
#define DDS_UTIL_HPP_INCLUDED | #define DDS_UTIL_HPP_INCLUDED | ||||
#include <algorithm> | |||||
#include <filesystem> | #include <filesystem> | ||||
#include <fstream> | #include <fstream> | ||||
#include <algorithm> | |||||
namespace dds { | namespace dds { | ||||
std::cerr << "An unhandled exception occured: " << e.what() << '\n'; \ | std::cerr << "An unhandled exception occured: " << e.what() << '\n'; \ | ||||
return 2; \ | return 2; \ | ||||
} \ | } \ | ||||
return ::dds::S_failed_checks; \ | |||||
return ::dds::S_failed_checks; \ | |||||
} \ | } \ | ||||
static_assert(true) | static_assert(true) | ||||