Files
XCEngine/editor/src/Core/ProjectRootResolver.h

166 lines
4.6 KiB
C++

#pragma once
#include "Platform/Win32Utf8.h"
#include <shellapi.h>
#include <filesystem>
#include <optional>
#include <string>
#include <system_error>
namespace XCEngine {
namespace Editor {
namespace fs = std::filesystem;
namespace detail {
inline fs::path NormalizePath(const fs::path& path, const fs::path& basePath = {}) {
if (path.empty()) {
return {};
}
fs::path absolutePath = path;
std::error_code ec;
if (absolutePath.is_relative()) {
absolutePath = basePath.empty()
? fs::absolute(absolutePath, ec)
: fs::absolute(basePath / absolutePath, ec);
if (ec) {
absolutePath = basePath.empty() ? absolutePath : (basePath / path);
}
}
ec.clear();
const fs::path canonicalPath = fs::weakly_canonical(absolutePath, ec);
if (!ec) {
return canonicalPath.lexically_normal();
}
return absolutePath.lexically_normal();
}
inline bool IsWorkspaceRoot(const fs::path& candidate) {
std::error_code ec;
return fs::exists(candidate / L"CMakeLists.txt", ec) &&
fs::is_directory(candidate / L"editor", ec) &&
fs::is_directory(candidate / L"engine", ec);
}
inline bool IsEditorProjectRoot(const fs::path& candidate) {
std::error_code ec;
return fs::exists(candidate / L"Project.xcproject", ec) ||
fs::is_directory(candidate / L"Assets", ec);
}
inline fs::path ResolveWorkspaceDefaultProjectRoot(const fs::path& workspaceRoot) {
if (workspaceRoot.empty()) {
return {};
}
const fs::path preferredProjectRoot = workspaceRoot / L"project";
if (IsEditorProjectRoot(preferredProjectRoot)) {
return NormalizePath(preferredProjectRoot);
}
if (IsEditorProjectRoot(workspaceRoot)) {
return NormalizePath(workspaceRoot);
}
return NormalizePath(preferredProjectRoot);
}
inline std::optional<fs::path> FindWorkspaceRoot(fs::path start) {
if (start.empty()) {
return std::nullopt;
}
start = NormalizePath(start);
for (fs::path current = start; !current.empty(); ) {
if (IsWorkspaceRoot(current)) {
return current;
}
const fs::path parent = current.parent_path();
if (parent == current) {
break;
}
current = parent;
}
return std::nullopt;
}
inline std::optional<fs::path> ParseCommandLineProjectOverride(const fs::path& workingDirectory) {
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (!argv || argc <= 1) {
if (argv) {
LocalFree(argv);
}
return std::nullopt;
}
std::optional<fs::path> projectOverride;
for (int i = 1; i < argc; ++i) {
const std::wstring arg = argv[i] ? argv[i] : L"";
if ((arg == L"--project" || arg == L"-p") && i + 1 < argc) {
projectOverride = NormalizePath(fs::path(argv[++i]), workingDirectory);
break;
}
static const std::wstring kProjectArgPrefix = L"--project=";
if (arg.rfind(kProjectArgPrefix, 0) == 0) {
projectOverride = NormalizePath(fs::path(arg.substr(kProjectArgPrefix.size())), workingDirectory);
break;
}
}
LocalFree(argv);
return projectOverride;
}
inline std::string PathToUtf8(const fs::path& path) {
return Platform::WideToUtf8(path.wstring());
}
} // namespace detail
inline std::string ResolveEditorProjectRootUtf8() {
std::error_code ec;
const fs::path workingDirectory = detail::NormalizePath(fs::current_path(ec));
const fs::path executableDirectory = detail::NormalizePath(fs::path(Platform::Utf8ToWide(Platform::GetExecutableDirectoryUtf8())));
if (const auto projectOverride = detail::ParseCommandLineProjectOverride(workingDirectory.empty() ? executableDirectory : workingDirectory)) {
return detail::PathToUtf8(*projectOverride);
}
if (const auto workspaceRoot = detail::FindWorkspaceRoot(workingDirectory)) {
return detail::PathToUtf8(detail::ResolveWorkspaceDefaultProjectRoot(*workspaceRoot));
}
if (const auto workspaceRoot = detail::FindWorkspaceRoot(executableDirectory)) {
return detail::PathToUtf8(detail::ResolveWorkspaceDefaultProjectRoot(*workspaceRoot));
}
if (!workingDirectory.empty()) {
return detail::PathToUtf8(workingDirectory);
}
return detail::PathToUtf8(executableDirectory);
}
inline bool SetEditorWorkingDirectory(const std::string& projectRootUtf8) {
if (projectRootUtf8.empty()) {
return false;
}
std::error_code ec;
fs::current_path(fs::path(Platform::Utf8ToWide(projectRootUtf8)), ec);
return !ec;
}
} // namespace Editor
} // namespace XCEngine