| #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) | ||||