additional tool work

This commit is contained in:
Jack Andersen 2015-06-10 23:41:10 -10:00
parent 6dda293cb9
commit 270468fb1f
6 changed files with 167 additions and 55 deletions

View File

@ -12,10 +12,6 @@ public:
{ {
} }
~ToolAdd()
{
}
static void Help(HelpOutput& help) static void Help(HelpOutput& help)
{ {
help.secHead(_S("NAME")); help.secHead(_S("NAME"));

View File

@ -11,19 +11,48 @@ public:
ToolInit(const ToolPassInfo& info) ToolInit(const ToolPassInfo& info)
: ToolBase(info) : ToolBase(info)
{ {
struct stat theStat;
const HECL::SystemString* dir;
if (info.args.size()) if (info.args.size())
{ dir = &info.args[0];
else
dir = &info.cwd;
if (HECL::Stat(dir->c_str(), &theStat))
{
throw HECL::Exception(_S("unable to stat '") + *dir + _S("'"));
return;
} }
m_dir = &info.args[0]; if (!S_ISDIR(theStat.st_mode))
{
throw HECL::Exception(_S("'") + *dir + _S("' is not a directory"));
return;
} }
~ToolInit() HECL::SystemString testPath = *dir + _S("/.hecl/index");
if (!HECL::Stat(testPath.c_str(), &theStat))
{ {
throw HECL::Exception(_S("project already exists at '") + *dir + _S("'"));
return;
}
m_dir = dir;
} }
int run() int run()
{ {
if (!m_dir)
return -1;
try
{
HECL::Database::Project(HECL::ProjectRootPath(*m_dir));
}
catch (HECL::Exception& e)
{
HECL::FPrintf(stderr, _S("unable to init project: '%s'\n"), e.swhat());
return -1;
}
HECL::Printf(_S("initialized project at '%s/.hecl'\n"), m_dir->c_str());
return 0; return 0;
} }

View File

@ -20,10 +20,12 @@
bool XTERM_COLOR = false; bool XTERM_COLOR = false;
/*
#define HECL_GIT 1234567 #define HECL_GIT 1234567
#define HECL_GIT_S "1234567" #define HECL_GIT_S "1234567"
#define HECL_BRANCH master #define HECL_BRANCH master
#define HECL_BRANCH_S "master" #define HECL_BRANCH_S "master"
*/
/* Main usage message */ /* Main usage message */
static void printHelp(const HECL::SystemChar* pname) static void printHelp(const HECL::SystemChar* pname)

View File

@ -7,6 +7,7 @@ char* win_realpath(const char* name, char* restrict resolved);
#include <stdlib.h> #include <stdlib.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/file.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#endif #endif
@ -156,12 +157,8 @@ static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLo
OVERLAPPED ov = {}; OVERLAPPED ov = {};
LockFileEx(fhandle, (lock == LWRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov); LockFileEx(fhandle, (lock == LWRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov);
#else #else
struct flock lk = if (flock(fileno(fp), ((lock == LWRITE) ? LOCK_EX : LOCK_SH) | LOCK_NB))
{ throw std::error_code(errno, std::system_category());
(short)((lock == LREAD) ? F_RDLCK : F_WRLCK),
SEEK_SET, 0, 0, 0
};
fcntl(fileno(fp), F_SETLK, &lk);
#endif #endif
} }

View File

@ -6,6 +6,7 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#include <map> #include <map>
#include <list>
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
#include <atomic> #include <atomic>
@ -172,6 +173,7 @@ public:
private: private:
ProjectRootPath m_rootPath; ProjectRootPath m_rootPath;
CompiledSpecs m_compiledSpecs; CompiledSpecs m_compiledSpecs;
FLogger m_logger;
public: public:
Project(const HECL::ProjectRootPath& rootPath); Project(const HECL::ProjectRootPath& rootPath);
@ -184,16 +186,16 @@ public:
class ConfigFile class ConfigFile
{ {
SystemString m_filepath; SystemString m_filepath;
std::vector<std::string> m_lines; std::list<std::string> m_lines;
FILE* m_lockedFile = NULL; FILE* m_lockedFile = NULL;
public: public:
ConfigFile(const Project& project, const SystemString& name); ConfigFile(const Project& project, const SystemString& name);
const std::vector<std::string>& lockAndRead(); std::list<std::string>& lockAndRead();
void addLine(const std::string& line); void addLine(const std::string& line);
void removeLine(const std::string& refLine); void removeLine(const std::string& refLine);
bool checkForLine(const std::string& refLine); bool checkForLine(const std::string& refLine);
void unlockAndDiscard(); void unlockAndDiscard();
void unlockAndCommit(); bool unlockAndCommit();
}; };
ConfigFile m_specs; ConfigFile m_specs;
ConfigFile m_paths; ConfigFile m_paths;
@ -233,7 +235,7 @@ public:
const std::vector<ProjectPath*> getChangedPaths(); const std::vector<ProjectPath*> getChangedPaths();
void addOrUpdatePath(const ProjectPath& path); void addOrUpdatePath(const ProjectPath& path);
void unlockAndDiscard(); void unlockAndDiscard();
void unlockAndCommit(bool onlyUpdated=false); bool unlockAndCommit(bool onlyUpdated=false);
}; };
IndexFile m_index; IndexFile m_index;

View File

@ -45,12 +45,12 @@ Project::ConfigFile::ConfigFile(const Project& project, const SystemString& name
m_filepath = project.m_rootPath.getAbsolutePath() + _S("/.hecl/config/") + name; m_filepath = project.m_rootPath.getAbsolutePath() + _S("/.hecl/config/") + name;
} }
const std::vector<std::string>& Project::ConfigFile::lockAndRead() std::list<std::string>& Project::ConfigFile::lockAndRead()
{ {
if (m_lockedFile) if (m_lockedFile)
return m_lines; return m_lines;
m_lockedFile = HECL::Fopen(m_filepath.c_str(), _S("r+"), LWRITE); m_lockedFile = HECL::Fopen(m_filepath.c_str(), _S("a+"), LWRITE);
std::string mainString; std::string mainString;
char readBuf[1024]; char readBuf[1024];
@ -93,7 +93,7 @@ void Project::ConfigFile::removeLine(const std::string& refLine)
if (!m_lockedFile) if (!m_lockedFile)
throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called"));
for (std::vector<std::string>::const_iterator it=m_lines.begin(); for (auto it = m_lines.begin();
it != m_lines.end(); it != m_lines.end();
++it) ++it)
{ {
@ -125,25 +125,41 @@ void Project::ConfigFile::unlockAndDiscard()
m_lockedFile = NULL; m_lockedFile = NULL;
} }
void Project::ConfigFile::unlockAndCommit() bool Project::ConfigFile::unlockAndCommit()
{ {
if (!m_lockedFile) if (!m_lockedFile)
throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called")); throw HECL::Exception(_S("Project::ConfigFile::lockAndRead not yet called"));
fseek(m_lockedFile, 0, SEEK_SET); SystemString newPath = m_filepath + _S(".part");
#if _WIN32 FILE* newFile = HECL::Fopen(newPath.c_str(), _S("w"), LWRITE);
SetEndOfFile((HANDLE)fileno(m_lockedFile)); bool fail = false;
#else
ftruncate(fileno(m_lockedFile), 0);
#endif
for (const std::string& line : m_lines) for (const std::string& line : m_lines)
{ {
fwrite(line.c_str(), 1, line.size(), m_lockedFile); if (fwrite(line.c_str(), 1, line.size(), newFile) != line.size())
fwrite("\n", 1, 1, m_lockedFile); {
fail = true;
break;
}
if (fwrite("\n", 1, 1, newFile) != 1)
{
fail = true;
break;
}
} }
m_lines.clear(); m_lines.clear();
fclose(newFile);
fclose(m_lockedFile); fclose(m_lockedFile);
m_lockedFile = NULL; m_lockedFile = NULL;
if (fail)
{
unlink(newPath.c_str());
return false;
}
else
{
rename(newPath.c_str(), m_filepath.c_str());
return true;
}
} }
/********************************************** /**********************************************
@ -175,19 +191,22 @@ const std::vector<Project::IndexFile::Entry>& Project::IndexFile::lockAndRead()
if (m_lockedFile) if (m_lockedFile)
return m_entryStore; return m_entryStore;
m_lockedFile = HECL::Fopen(m_filepath.c_str(), _S("r+"), LWRITE); /* Open file and begin lock cycle */
m_lockedFile = HECL::Fopen(m_filepath.c_str(), _S("a+b"), LWRITE);
m_maxPathLen = 0; m_maxPathLen = 0;
m_onlyUpdatedMaxPathLen = 0; m_onlyUpdatedMaxPathLen = 0;
/* Read index header */
SIndexHeader header; SIndexHeader header;
if (fread(&header, 1, sizeof(header), m_lockedFile) != sizeof(header)) if (fread(&header, 1, sizeof(header), m_lockedFile) != sizeof(header))
return m_entryStore; return m_entryStore; /* Not yet written, this commit will take care of it */
header.swapWithNative(); header.swapWithNative();
if (header.magic != "HECL") if (header.magic != "HECL")
throw HECL::Exception(_S("unrecognized HECL index")); throw HECL::Exception(_S("unrecognized HECL index"));
if (header.version != 1) if (header.version != 1)
throw HECL::Exception(_S("unrecognized HECL version")); throw HECL::Exception(_S("unrecognized HECL version"));
/* Iterate existing index entries */
char* pathBuf = new char[header.maxPathLen]; char* pathBuf = new char[header.maxPathLen];
for (uint32_t e=0 ; e<header.entryCount ; ++e) for (uint32_t e=0 ; e<header.entryCount ; ++e)
{ {
@ -205,7 +224,7 @@ const std::vector<Project::IndexFile::Entry>& Project::IndexFile::lockAndRead()
ProjectPath path(m_project.getProjectRootPath(), pathView.sys_str()); ProjectPath path(m_project.getProjectRootPath(), pathView.sys_str());
if (m_entryLookup.find(path) == m_entryLookup.end()) if (m_entryLookup.find(path) == m_entryLookup.end())
{ {
m_entryStore.push_back(Project::IndexFile::Entry(path, mt)); m_entryStore.push_back(Entry(path, mt));
m_entryLookup[path] = &m_entryStore.back(); m_entryLookup[path] = &m_entryStore.back();
} }
} }
@ -219,7 +238,7 @@ const std::vector<ProjectPath*> Project::IndexFile::getChangedPaths()
throw HECL::Exception(_S("Project::IndexFile::lockAndRead not yet called")); throw HECL::Exception(_S("Project::IndexFile::lockAndRead not yet called"));
std::vector<ProjectPath*> retval; std::vector<ProjectPath*> retval;
for (Project::IndexFile::Entry& ent : m_entryStore) for (Entry& ent : m_entryStore)
if (ent.m_lastModtime != ent.m_path.getModtime()) if (ent.m_lastModtime != ent.m_path.getModtime())
retval.push_back(&ent.m_path); retval.push_back(&ent.m_path);
return retval; return retval;
@ -237,7 +256,7 @@ void Project::IndexFile::addOrUpdatePath(const ProjectPath& path)
std::unordered_map<ProjectPath, Entry*>::iterator it = m_entryLookup.find(path); std::unordered_map<ProjectPath, Entry*>::iterator it = m_entryLookup.find(path);
if (it == m_entryLookup.end()) if (it == m_entryLookup.end())
{ {
m_entryStore.push_back(Project::IndexFile::Entry(path, path.getModtime())); m_entryStore.push_back(Entry(path, path.getModtime()));
m_entryLookup[path] = &m_entryStore.back(); m_entryLookup[path] = &m_entryStore.back();
m_entryStore.back().m_updated = true; m_entryStore.back().m_updated = true;
return; return;
@ -257,18 +276,13 @@ void Project::IndexFile::unlockAndDiscard()
m_lockedFile = NULL; m_lockedFile = NULL;
} }
void Project::IndexFile::unlockAndCommit(bool onlyUpdated) bool Project::IndexFile::unlockAndCommit(bool onlyUpdated)
{ {
if (!m_lockedFile) if (!m_lockedFile)
throw HECL::Exception(_S("Project::IndexFile::lockAndRead not yet called")); throw HECL::Exception(_S("Project::IndexFile::lockAndRead not yet called"));
fseek(m_lockedFile, 0, SEEK_SET); SystemString newPath = m_filepath + _S(".part");
#if _WIN32 FILE* newFile = HECL::Fopen(newPath.c_str(), _S("wb"), LWRITE);
SetEndOfFile((HANDLE)fileno(m_lockedFile));
#else
ftruncate(fileno(m_lockedFile), 0);
#endif
SIndexHeader header = SIndexHeader header =
{ {
HECL::FourCC("HECL"), 1, HECL::FourCC("HECL"), 1,
@ -276,25 +290,53 @@ void Project::IndexFile::unlockAndCommit(bool onlyUpdated)
(uint32_t)(onlyUpdated ? m_onlyUpdatedMaxPathLen : m_maxPathLen) (uint32_t)(onlyUpdated ? m_onlyUpdatedMaxPathLen : m_maxPathLen)
}; };
header.swapWithNative(); header.swapWithNative();
fwrite(&header, 1, sizeof(header), m_lockedFile); bool fail = false;
if (fwrite(&header, 1, sizeof(header), newFile) != sizeof(header))
fail = true;
for (Project::IndexFile::Entry& ent : m_entryStore) if (!fail)
{
for (Entry& ent : m_entryStore)
{ {
if (!onlyUpdated || ent.m_updated) if (!onlyUpdated || ent.m_updated)
{ {
uint64_t mt = ToBig(ent.m_lastModtime.getTs()); uint64_t mt = ToBig(ent.m_lastModtime.getTs());
fwrite(&mt, 1, 8, m_lockedFile); if (fwrite(&mt, 1, 8, newFile) != 8)
{
fail = true;
break;
}
size_t strLen = ent.m_path.getRelativePathUTF8().size(); size_t strLen = ent.m_path.getRelativePathUTF8().size();
uint32_t strLenb = ToBig(strLen); uint32_t strLenb = ToBig(strLen);
fwrite(&strLenb, 1, 4, m_lockedFile); if (fwrite(&strLenb, 1, 4, newFile) != 4)
fwrite(ent.m_path.getRelativePathUTF8().c_str(), 1, strLen, m_lockedFile); {
fail = true;
break;
}
if (fwrite(ent.m_path.getRelativePathUTF8().c_str(), 1, strLen, newFile) != strLen)
{
fail = true;
break;
}
}
} }
} }
m_entryLookup.clear(); m_entryLookup.clear();
m_entryStore.clear(); m_entryStore.clear();
fclose(newFile);
fclose(m_lockedFile); fclose(m_lockedFile);
m_lockedFile = NULL; m_lockedFile = NULL;
if (fail)
{
unlink(newPath.c_str());
return false;
}
else
{
rename(newPath.c_str(), m_filepath.c_str());
return true;
}
} }
/********************************************** /**********************************************
@ -321,34 +363,78 @@ Project::Project(const ProjectRootPath& rootPath)
HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl")); HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl"));
HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/cooked")); HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/cooked"));
HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/config")); HECL::MakeDir(m_rootPath.getAbsolutePath() + _S("/.hecl/config"));
/* Ensure index is initialized */
if (m_index.lockAndRead().empty())
m_index.unlockAndCommit();
else
m_index.unlockAndDiscard();
} }
void Project::registerLogger(FLogger logger) void Project::registerLogger(FLogger logger)
{ {
m_logger = logger;
} }
bool Project::addPaths(const std::vector<ProjectPath>& paths) bool Project::addPaths(const std::vector<ProjectPath>& paths)
{ {
m_paths.lockAndRead();
for (const ProjectPath& path : paths)
m_paths.addLine(path.getRelativePathUTF8());
return m_paths.unlockAndCommit();
} }
bool Project::removePaths(const std::vector<ProjectPath>& paths, bool recursive) bool Project::removePaths(const std::vector<ProjectPath>& paths, bool recursive)
{ {
std::list<std::string>& 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) 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) bool Project::removeGroup(const ProjectPath& path)
{ {
m_groups.lockAndRead();
m_groups.removeLine(path.getRelativePathUTF8());
return m_groups.unlockAndCommit();
} }
bool Project::enableDataSpecs(const std::vector<std::string>& specs) bool Project::enableDataSpecs(const std::vector<SystemString>& specs)
{ {
m_specs.lockAndRead();
for (const SystemString& spec : specs)
m_specs.addLine(spec);
return m_specs.unlockAndCommit();
} }
bool Project::disableDataSpecs(const std::vector<std::string>& specs) bool Project::disableDataSpecs(const std::vector<SystemString>& specs)
{ {
m_specs.lockAndRead();
for (const SystemString& spec : specs)
m_specs.removeLine(spec);
return m_specs.unlockAndCommit();
} }
bool Project::cookPath(const ProjectPath& path, bool Project::cookPath(const ProjectPath& path,