#include #include #include #include #include #if _WIN32 #else #include #endif #include "HECLDatabase.hpp" namespace HECL { namespace Database { /********************************************** * Project::ConfigFile **********************************************/ static inline bool CheckNewLineAdvance(std::string::const_iterator& it) { if (*it == '\n') { it += 1; return true; } else if (*it == '\r') { if (*(it+1) == '\n') { it += 2; return true; } it += 1; return true; } return false; } Project::ConfigFile::ConfigFile(const Project& project, const SystemString& name, const SystemString& subdir) { m_filepath = project.m_rootPath.getAbsolutePath() + subdir + name; } std::list& Project::ConfigFile::lockAndRead() { if (m_lockedFile) return m_lines; m_lockedFile = HECL::Fopen(m_filepath.c_str(), _S("a+"), LWRITE); std::string mainString; char readBuf[1024]; size_t readSz; while ((readSz = fread(readBuf, 1, 1024, m_lockedFile))) mainString += std::string(readBuf, readSz); std::string::const_iterator begin = mainString.begin(); std::string::const_iterator end = mainString.begin(); m_lines.clear(); while (end != mainString.end()) { std::string::const_iterator origEnd = end; if (*end == '\0') break; else if (CheckNewLineAdvance(end)) { if (begin != origEnd) m_lines.push_back(std::string(begin, origEnd)); begin = end; continue; } ++end; } if (begin != end) m_lines.push_back(std::string(begin, end)); return m_lines; } void Project::ConfigFile::addLine(const std::string& line) { if (!checkForLine(line)) m_lines.push_back(line); } void Project::ConfigFile::removeLine(const std::string& refLine) { if (!m_lockedFile) throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); for (auto it = m_lines.begin(); it != m_lines.end(); ++it) { if (!(*it).compare(refLine)) it = m_lines.erase(it); } } bool Project::ConfigFile::checkForLine(const std::string& refLine) { if (!m_lockedFile) throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); for (const std::string& line : m_lines) { if (!line.compare(refLine)) return true; } return false; } void Project::ConfigFile::unlockAndDiscard() { if (!m_lockedFile) throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); m_lines.clear(); fclose(m_lockedFile); m_lockedFile = NULL; } bool Project::ConfigFile::unlockAndCommit() { if (!m_lockedFile) throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); SystemString newPath = m_filepath + _S(".part"); FILE* newFile = HECL::Fopen(newPath.c_str(), _S("w"), LWRITE); bool fail = false; for (const std::string& line : m_lines) { if (fwrite(line.c_str(), 1, line.size(), newFile) != line.size()) { fail = true; break; } if (fwrite("\n", 1, 1, newFile) != 1) { fail = true; break; } } m_lines.clear(); fclose(newFile); fclose(m_lockedFile); m_lockedFile = NULL; if (fail) { unlink(newPath.c_str()); return false; } else { rename(newPath.c_str(), m_filepath.c_str()); return true; } } /********************************************** * Project **********************************************/ Project::Project(const ProjectRootPath& rootPath) : m_rootPath(rootPath), m_specs(*this, _S("specs")), m_paths(*this, _S("paths")), m_groups(*this, _S("groups")) { /* Stat for existing project directory (must already exist) */ struct stat myStat; if (HECL::Stat(m_rootPath.getAbsolutePath().c_str(), &myStat)) throw std::error_code(errno, std::system_category()); if (!S_ISDIR(myStat.st_mode)) throw std::invalid_argument("provided path must be a directory; '" + m_rootPath.getAbsolutePathUTF8() + "' isn't"); /* Create project directory structure */ HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl")); HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/cooked")); /* Ensure beacon is valid or created */ FILE* bf = HECL::Fopen((m_rootPath.getAbsolutePath() + _S("/.hecl/beacon")).c_str(), _S("a+b")); struct BeaconStruct { FourCC magic; uint32_t version; } beacon; #define DATA_VERSION 1 static const FourCC hecl("HECL"); if (fread(&beacon, 1, sizeof(beacon), bf) != sizeof(beacon)) { fseek(bf, 0, SEEK_SET); beacon.magic = hecl; beacon.version = SBig(DATA_VERSION); fwrite(&beacon, 1, sizeof(beacon), bf); } fclose(bf); if (beacon.magic != hecl || SBig(beacon.version) != DATA_VERSION) throw Exception(_S("incompatible HECL project")); /* Compile current dataspec */ rescanDataSpecs(); } void Project::registerLogger(FLogger logger) { m_logger = logger; } bool Project::addPaths(const std::vector& paths) { m_paths.lockAndRead(); for (const ProjectPath& path : paths) m_paths.addLine(path.getRelativePathUTF8()); return m_paths.unlockAndCommit(); } bool Project::removePaths(const std::vector& paths, bool recursive) { std::list& existingPaths = m_paths.lockAndRead(); if (recursive) { for (const ProjectPath& path : paths) { std::string recursiveBase = path.getRelativePathUTF8(); for (auto it = existingPaths.begin(); it != existingPaths.end(); ++it) { if (!(*it).compare(0, recursiveBase.size(), recursiveBase)) it = existingPaths.erase(it); } } } else for (const ProjectPath& path : paths) m_paths.removeLine(path.getRelativePathUTF8()); return m_paths.unlockAndCommit(); } bool Project::addGroup(const HECL::ProjectPath& path) { m_groups.lockAndRead(); m_groups.addLine(path.getRelativePathUTF8()); return m_groups.unlockAndCommit(); } bool Project::removeGroup(const ProjectPath& path) { m_groups.lockAndRead(); m_groups.removeLine(path.getRelativePathUTF8()); return m_groups.unlockAndCommit(); } void Project::rescanDataSpecs() { m_compiledSpecs.clear(); m_specs.lockAndRead(); for (const DataSpecEntry* spec = DATA_SPEC_REGISTRY; spec->name.size(); ++spec) { SystemUTF8View specUTF8(spec->name); m_compiledSpecs.push_back({*spec, m_specs.checkForLine(specUTF8.utf8_str()) ? true : false}); } m_specs.unlockAndDiscard(); } bool Project::enableDataSpecs(const std::vector& specs) { m_specs.lockAndRead(); for (const SystemString& spec : specs) m_specs.addLine(spec); return m_specs.unlockAndCommit(); } bool Project::disableDataSpecs(const std::vector& specs) { m_specs.lockAndRead(); for (const SystemString& spec : specs) m_specs.removeLine(spec); return m_specs.unlockAndCommit(); } bool Project::cookPath(const ProjectPath& path, std::function feedbackCb, bool recursive) { } void Project::interruptCook() { } bool Project::cleanPath(const ProjectPath& path, bool recursive) { } PackageDepsgraph Project::buildPackageDepsgraph(const ProjectPath& path) { } } }