metaforce/hecl/driver/ToolPackage.hpp

186 lines
6.5 KiB
C++
Raw Normal View History

2018-10-07 03:38:44 +00:00
#pragma once
2015-06-10 02:40:03 +00:00
#include <vector>
#include <string>
#include "ToolBase.hpp"
2017-12-29 07:56:31 +00:00
#include <cstdio>
2015-06-10 02:40:03 +00:00
2018-12-08 05:18:42 +00:00
class ToolPackage final : public ToolBase {
std::vector<hecl::ProjectPath> m_selectedItems;
std::unique_ptr<hecl::Database::Project> m_fallbackProj;
hecl::Database::Project* m_useProj;
const hecl::Database::DataSpecEntry* m_spec = nullptr;
bool m_fast = false;
void AddSelectedItem(const hecl::ProjectPath& path) {
for (const hecl::ProjectPath& item : m_selectedItems)
if (item == path)
return;
m_selectedItems.push_back(path);
}
void CheckFile(const hecl::ProjectPath& path) {
2019-10-01 07:23:35 +00:00
auto lastComp = path.getLastComponent();
if (hecl::StringUtils::BeginsWith(lastComp, "!world") &&
hecl::StringUtils::EndsWith(lastComp, ".blend"))
2018-12-08 05:18:42 +00:00
AddSelectedItem(path);
}
2017-10-27 10:10:08 +00:00
2018-12-08 05:18:42 +00:00
void FindSelectedItems(const hecl::ProjectPath& path, bool checkGeneral) {
if (path.isFile()) {
CheckFile(path);
return;
}
2017-10-25 07:46:32 +00:00
2018-12-08 05:18:42 +00:00
size_t origSize = m_selectedItems.size();
hecl::DirectoryEnumerator dEnum(path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsThenFilesSorted, false,
false, true);
for (const auto& ent : dEnum) {
hecl::ProjectPath childPath(path, ent.m_name);
if (ent.m_isDir)
FindSelectedItems(childPath, checkGeneral && childPath.getPathComponents().size() <= 2);
else
CheckFile(childPath);
}
2017-10-25 07:46:32 +00:00
2018-12-08 05:18:42 +00:00
/* Directory with 2 components not "Shared" or macOS app bundle
* and no nested !world.blend files == General PAK */
if (checkGeneral && origSize == m_selectedItems.size()) {
auto pathComps = path.getPathComponents();
if (pathComps.size() == 2 && pathComps[0] != "out" && pathComps[1] != "Shared" &&
pathComps[0].find(".app") == std::string::npos)
2018-12-08 05:18:42 +00:00
AddSelectedItem(path);
2017-10-25 07:46:32 +00:00
}
2018-12-08 05:18:42 +00:00
}
2017-10-25 07:46:32 +00:00
2015-06-10 02:40:03 +00:00
public:
explicit ToolPackage(const ToolPassInfo& info) : ToolBase(info), m_useProj(info.project) {
2018-12-08 05:18:42 +00:00
if (!info.project)
2020-04-11 22:48:11 +00:00
LogModule.report(logvisor::Fatal, FMT_STRING("hecl package must be ran within a project directory"));
2018-12-08 05:18:42 +00:00
/* Scan args */
if (info.args.size()) {
/* See if project path is supplied via args and use that over the getcwd one */
m_selectedItems.reserve(info.args.size());
for (const std::string& arg : info.args) {
2018-12-08 05:18:42 +00:00
if (arg.empty())
continue;
else if (arg == "--fast") {
2018-12-08 05:18:42 +00:00
m_fast = true;
continue;
} else if (arg.size() >= 8 && !arg.compare(0, 7, "--spec=")) {
std::string specName(arg.begin() + 7, arg.end());
2018-12-08 05:18:42 +00:00
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!hecl::StrCaseCmp(spec->m_name.data(), specName.c_str())) {
m_spec = spec;
break;
2017-10-25 07:46:32 +00:00
}
2018-12-08 05:18:42 +00:00
}
if (!m_spec)
LogModule.report(logvisor::Fatal, FMT_STRING("unable to find data spec '{}'"), specName);
2018-12-08 05:18:42 +00:00
continue;
} else if (arg.size() >= 2 && arg[0] == '-' && arg[1] == '-')
2018-12-08 05:18:42 +00:00
continue;
std::string subPath;
2018-12-08 05:18:42 +00:00
hecl::ProjectRootPath root = hecl::SearchForProject(MakePathArgAbsolute(arg, info.cwd), subPath);
if (root) {
if (!m_fallbackProj) {
m_fallbackProj.reset(new hecl::Database::Project(root));
m_useProj = m_fallbackProj.get();
} else if (m_fallbackProj->getProjectRootPath() != root)
2017-10-25 07:46:32 +00:00
LogModule.report(logvisor::Fatal,
FMT_STRING("hecl package can only process multiple items in the same project; "
"'{}' and '{}' are different projects"),
2019-07-20 04:22:58 +00:00
m_fallbackProj->getProjectRootPath().getAbsolutePath(),
root.getAbsolutePath());
2017-10-25 07:46:32 +00:00
2018-12-08 05:18:42 +00:00
FindSelectedItems({*m_useProj, subPath}, true);
}
}
2015-06-10 02:40:03 +00:00
}
2018-12-08 05:18:42 +00:00
if (!m_useProj)
LogModule.report(logvisor::Fatal,
2020-04-11 22:48:11 +00:00
FMT_STRING("hecl package must be ran within a project directory or "
2019-07-20 04:22:58 +00:00
"provided a path within a project"));
2018-12-08 05:18:42 +00:00
/* Default case: recursive at root */
if (m_selectedItems.empty())
FindSelectedItems({*m_useProj, ""}, true);
2018-12-08 05:18:42 +00:00
}
static void Help(HelpOutput& help) {
help.secHead("NAME");
2018-12-08 05:18:42 +00:00
help.beginWrap();
help.wrap(
"hecl-pack\n"
"hecl-package - Package objects within the project database\n");
2018-12-08 05:18:42 +00:00
help.endWrap();
help.secHead("SYNOPSIS");
2018-12-08 05:18:42 +00:00
help.beginWrap();
help.wrap("hecl package [--spec=<spec>] [<input-dir>]\n");
2018-12-08 05:18:42 +00:00
help.endWrap();
help.secHead("DESCRIPTION");
2018-12-08 05:18:42 +00:00
help.beginWrap();
help.wrap(
"This command initiates a packaging pass on the project database. Packaging "
"is analogous to linking in software development. All objects necessary to "
"generate a complete package are gathered, grouped, and indexed within a .upak file.\n");
2018-12-08 05:18:42 +00:00
help.endWrap();
help.secHead("OPTIONS");
help.optionHead("--spec=<spec>", "data specification");
2018-12-08 05:18:42 +00:00
help.beginWrap();
help.wrap(
"Specifies a DataSpec to use when cooking and generating the package. "
"This build of hecl supports the following values of <spec>:\n");
2018-12-08 05:18:42 +00:00
for (const hecl::Database::DataSpecEntry* spec : hecl::Database::DATA_SPEC_REGISTRY) {
if (!spec->m_factory)
continue;
help.wrap(" ");
2018-12-08 05:18:42 +00:00
help.wrapBold(spec->m_name.data());
help.wrap("\n");
2018-12-08 05:18:42 +00:00
}
help.endWrap();
2015-06-10 02:40:03 +00:00
help.secHead("OPTIONS");
help.optionHead("<input-dir>", "input directory");
2018-12-08 05:18:42 +00:00
help.beginWrap();
help.wrap(
"Specifies a project subdirectory to root the resulting package from. "
"If any dependent files fall outside this subdirectory, they will be implicitly "
"gathered and packaged.\n");
2018-12-08 05:18:42 +00:00
help.endWrap();
}
std::string_view toolName() const override { return "package"sv; }
2018-12-08 05:18:42 +00:00
int run() override {
2018-12-08 05:18:42 +00:00
if (XTERM_COLOR)
fmt::print(FMT_STRING("" GREEN BOLD "ABOUT TO PACKAGE:" NORMAL "\n"));
2018-12-08 05:18:42 +00:00
else
fmt::print(FMT_STRING("ABOUT TO PACKAGE:\n"));
2018-12-08 05:18:42 +00:00
for (auto& item : m_selectedItems)
fmt::print(FMT_STRING(" {}\n"), item.getRelativePath());
2018-12-08 05:18:42 +00:00
fflush(stdout);
if (continuePrompt()) {
hecl::MultiProgressPrinter printer(true);
hecl::ClientProcess cp(&printer);
for (const hecl::ProjectPath& path : m_selectedItems) {
if (!m_useProj->packagePath(path, printer, m_fast, m_spec, &cp))
LogModule.report(logvisor::Error, FMT_STRING("Unable to package {}"), path.getAbsolutePath());
2018-12-08 05:18:42 +00:00
}
cp.waitUntilComplete();
2015-06-10 02:40:03 +00:00
}
2018-12-08 05:18:42 +00:00
return 0;
}
void cancel() override { m_useProj->interruptCook(); }
2015-06-10 02:40:03 +00:00
};