add linux file watcher and test runner script
This commit is contained in:
parent
475c1cf8d6
commit
8dc6ca9a09
295
platform.d
295
platform.d
@ -1,14 +1,15 @@
|
|||||||
module dlib.platform;
|
module dlib.platform;
|
||||||
|
|
||||||
import dlib.aliases;
|
import dlib.aliases;
|
||||||
|
import dlib.alloc;
|
||||||
|
import dlib.util;
|
||||||
|
|
||||||
import includes;
|
import includes;
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
import core.memory;
|
import core.memory;
|
||||||
import core.thread.osthread;
|
import core.thread.osthread;
|
||||||
import core.time;
|
import core.time;
|
||||||
|
|
||||||
@nogc:
|
|
||||||
|
|
||||||
const WINDOW_EDGE_BUFFER = 50;
|
const WINDOW_EDGE_BUFFER = 50;
|
||||||
|
|
||||||
enum Input
|
enum Input
|
||||||
@ -45,6 +46,11 @@ version(linux)
|
|||||||
{
|
{
|
||||||
import core.sys.posix.dlfcn;
|
import core.sys.posix.dlfcn;
|
||||||
import core.sys.posix.sys.mman;
|
import core.sys.posix.sys.mman;
|
||||||
|
import core.sys.linux.sys.inotify;
|
||||||
|
import core.sys.linux.fcntl;
|
||||||
|
import core.sys.posix.unistd;
|
||||||
|
|
||||||
|
import core.stdc.string : strlen;
|
||||||
|
|
||||||
struct InputEvent
|
struct InputEvent
|
||||||
{
|
{
|
||||||
@ -616,6 +622,291 @@ MemFree(void* ptr, u64 size)
|
|||||||
assert(munmap(ptr, size) == 0, "MemFree failure");
|
assert(munmap(ptr, size) == 0, "MemFree failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Watcher
|
||||||
|
{
|
||||||
|
Arena arena;
|
||||||
|
u8[] buffer;
|
||||||
|
WatcherH handle;
|
||||||
|
WatchH dir_handle;
|
||||||
|
u8[] watched_dir;
|
||||||
|
bool blocking;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias WatcherH = int;
|
||||||
|
alias WatchH = int;
|
||||||
|
|
||||||
|
enum WatchType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Access = IN_ACCESS,
|
||||||
|
Metadata = IN_ATTRIB,
|
||||||
|
Create = IN_CREATE,
|
||||||
|
Delete = IN_DELETE,
|
||||||
|
Modify = IN_MODIFY,
|
||||||
|
Moved = IN_MOVED_FROM | IN_MOVED_TO,
|
||||||
|
}
|
||||||
|
|
||||||
|
alias WT = WatchType;
|
||||||
|
|
||||||
|
Watcher
|
||||||
|
WatchDirectory(string dir, WatchType type, bool blocking = false)
|
||||||
|
{
|
||||||
|
assert(dir.length > 0);
|
||||||
|
|
||||||
|
Watcher watcher = {
|
||||||
|
arena: CreateArena(MB(4)),
|
||||||
|
buffer: AllocArray!(u8)(MB(1)),
|
||||||
|
blocking: blocking,
|
||||||
|
watched_dir: (cast(u8*)dir.ptr)[0 .. dir.length],
|
||||||
|
};
|
||||||
|
|
||||||
|
watcher.handle = inotify_init();
|
||||||
|
assert(watcher.dir_handle >= 0, "WatchDirectory failure: unable to initialize");
|
||||||
|
|
||||||
|
if (!blocking)
|
||||||
|
{
|
||||||
|
fcntl(watcher.handle, F_SETFL, fcntl(watcher.handle, F_GETFL) | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher.dir_handle = inotify_add_watch(watcher.handle, dir.ptr, IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MODIFY|IN_MOVE_SELF|IN_MOVED_FROM|IN_MOVED_TO|IN_ATTRIB|IN_EXCL_UNLINK);
|
||||||
|
|
||||||
|
return watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchEvent[]
|
||||||
|
ViewChanges(Watcher* watcher)
|
||||||
|
{
|
||||||
|
assert(watcher.handle >= 0 && watcher.dir_handle >= 0, "ViewChanges failure: handles are not valid");
|
||||||
|
|
||||||
|
Reset(&watcher.arena);
|
||||||
|
|
||||||
|
WatchEvent[] events;
|
||||||
|
|
||||||
|
i64 length = read(watcher.handle, watcher.buffer.ptr, watcher.buffer.length);
|
||||||
|
if (length > 0)
|
||||||
|
{
|
||||||
|
i64 count = 0;
|
||||||
|
i64 i = 0;
|
||||||
|
while(i < length)
|
||||||
|
{
|
||||||
|
inotify_event* event = (cast(inotify_event*)(watcher.buffer.ptr + i));
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
assert(event.wd == watcher.dir_handle);
|
||||||
|
|
||||||
|
i += inotify_event.sizeof + event.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
struct Moved
|
||||||
|
{
|
||||||
|
u32 cookie;
|
||||||
|
i64 to;
|
||||||
|
i64 from;
|
||||||
|
}
|
||||||
|
|
||||||
|
Moved[] moved = AllocArray!(Moved)(&watcher.arena, (count/2)+1);
|
||||||
|
i64 m_count = 0;
|
||||||
|
events = AllocArray!(WatchEvent)(&watcher.arena, count);
|
||||||
|
count = 0;
|
||||||
|
i = 0;
|
||||||
|
while (i < length)
|
||||||
|
{
|
||||||
|
inotify_event* event = (cast(inotify_event*)(watcher.buffer.ptr + i));
|
||||||
|
if (event.len > 0)
|
||||||
|
{
|
||||||
|
u8[] file_name = (cast(u8*)event.name)[0 .. strlen(event.name.ptr)];
|
||||||
|
|
||||||
|
if (event.mask & IN_MOVED_FROM || event.mask & IN_MOVED_TO)
|
||||||
|
{
|
||||||
|
bool from = (event.mask & IN_MOVED_FROM) > 0;
|
||||||
|
|
||||||
|
Moved* m;
|
||||||
|
foreach(j; 0 .. m_count)
|
||||||
|
{
|
||||||
|
if (moved[j].cookie == event.cookie)
|
||||||
|
{
|
||||||
|
m = moved.ptr + j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m != null)
|
||||||
|
{
|
||||||
|
if (from && m.to >= 0)
|
||||||
|
{
|
||||||
|
events[m.to].names[0] = file_name;
|
||||||
|
|
||||||
|
if (watcher.watched_dir == file_name)
|
||||||
|
{
|
||||||
|
events[m.to].type &= ~(WET.File | WET.Dir);
|
||||||
|
events[m.to].type |= WET.Self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!from && m.from >= 0)
|
||||||
|
{
|
||||||
|
events[m.from].names[1] = file_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WatchEvent* ev = events.ptr + count;
|
||||||
|
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||||
|
ev.type |= WET.Moved;
|
||||||
|
|
||||||
|
moved[m_count].cookie = event.cookie;
|
||||||
|
|
||||||
|
if (from)
|
||||||
|
{
|
||||||
|
ev.names[0] = file_name;
|
||||||
|
moved[m_count].from = count;
|
||||||
|
moved[m_count].to = -1;
|
||||||
|
|
||||||
|
if (watcher.watched_dir == file_name)
|
||||||
|
{
|
||||||
|
ev.type &= ~(WET.File | WET.Dir);
|
||||||
|
ev.type |= WET.Self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ev.names[1] = file_name;
|
||||||
|
moved[m_count].to = count;
|
||||||
|
moved[m_count].from = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
m_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WatchEvent* ev = events.ptr + count;
|
||||||
|
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||||
|
|
||||||
|
ev.names[0] = file_name;
|
||||||
|
if (ev.names[0] == watcher.watched_dir)
|
||||||
|
{
|
||||||
|
ev.type = WET.Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEventType(ev, event.mask);
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WatchEvent* ev = events.ptr + count;
|
||||||
|
ev.type = (event.mask & IN_ISDIR) ? WET.Dir : WET.File;
|
||||||
|
|
||||||
|
SetEventType(ev, event.mask);
|
||||||
|
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += inotify_event.sizeof + event.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetEventType(WatchEvent* ev, u32 mask)
|
||||||
|
{
|
||||||
|
if (mask & IN_ACCESS)
|
||||||
|
{
|
||||||
|
ev.type |= WET.Accessed;
|
||||||
|
}
|
||||||
|
else if (mask & IN_ATTRIB)
|
||||||
|
{
|
||||||
|
ev.type |= WET.Metadata;
|
||||||
|
}
|
||||||
|
else if (mask & IN_CREATE)
|
||||||
|
{
|
||||||
|
ev.type |= WET.Created;
|
||||||
|
}
|
||||||
|
else if (mask & IN_MODIFY)
|
||||||
|
{
|
||||||
|
ev.type |= WET.Modified;
|
||||||
|
}
|
||||||
|
else if (mask & IN_DELETE)
|
||||||
|
{
|
||||||
|
ev.type |= WET.Deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.stdio;
|
||||||
|
import std.file : Remove = remove;
|
||||||
|
|
||||||
|
Watcher fw = WatchDirectory("./", WT.Create | WT.Delete | WT.Moved | WT.Modify | WT.Access | WT.Metadata | WT.Access | WT.Metadata);
|
||||||
|
|
||||||
|
auto f = File("test_file.txt", "wb");
|
||||||
|
|
||||||
|
f.write("test");
|
||||||
|
f.sync();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
Remove("./test_file.txt");
|
||||||
|
|
||||||
|
WatchEvent[] events = ViewChanges(&fw);
|
||||||
|
|
||||||
|
assert(events.length == 3);
|
||||||
|
|
||||||
|
assert(events[0].type == WET.FileCreated);
|
||||||
|
assert(events[0].names[0] == r"test_file.txt");
|
||||||
|
|
||||||
|
assert(events[1].type == WET.FileModified);
|
||||||
|
assert(events[1].names[0] == r"test_file.txt");
|
||||||
|
|
||||||
|
assert(events[2].type == WET.FileDeleted);
|
||||||
|
assert(events[2].names[0] == r"test_file.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WatchEventType
|
||||||
|
{
|
||||||
|
None = 0x0000,
|
||||||
|
File = 0x0001,
|
||||||
|
Dir = 0x0002,
|
||||||
|
Self = 0x0004,
|
||||||
|
Created = 0x0008,
|
||||||
|
Modified = 0x0010,
|
||||||
|
Deleted = 0x0020,
|
||||||
|
Moved = 0x0040,
|
||||||
|
Metadata = 0x0080,
|
||||||
|
Accessed = 0x0100,
|
||||||
|
FileCreated = WET.File | WET.Created,
|
||||||
|
FileModified = WET.File | WET.Modified,
|
||||||
|
FileDeleted = WET.File | WET.Deleted,
|
||||||
|
FileMoved = WET.File | WET.Moved,
|
||||||
|
FileMetadata = WET.File | WET.Metadata,
|
||||||
|
FileAccessed = WET.File | WET.Accessed,
|
||||||
|
DirCreated = WET.Dir | WET.Created,
|
||||||
|
DirModified = WET.Dir | WET.Modified,
|
||||||
|
DirDeleted = WET.Dir | WET.Deleted,
|
||||||
|
DirMoved = WET.Dir | WET.Moved,
|
||||||
|
DirMetadata = WET.Dir | WET.Metadata,
|
||||||
|
DirAccessed = WET.Dir | WET.Accessed,
|
||||||
|
SelfCreated = WET.Self | WET.Created,
|
||||||
|
SelfModified = WET.Self | WET.Modified,
|
||||||
|
SelfDeleted = WET.Self | WET.Deleted,
|
||||||
|
SelfMoved = WET.Self | WET.Moved,
|
||||||
|
SelfMetadata = WET.Self | WET.Metadata,
|
||||||
|
SelfAccessed = WET.Self | WET.Accessed,
|
||||||
|
}
|
||||||
|
|
||||||
|
alias WET = WatchEventType;
|
||||||
|
|
||||||
|
struct WatchEvent
|
||||||
|
{
|
||||||
|
WatchEventType type;
|
||||||
|
u8[][2] names;
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows)
|
version(Windows)
|
||||||
|
|||||||
9
test.sh
Executable file
9
test.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
name="Test_Runner"
|
||||||
|
|
||||||
|
ldc2 platform.d aliases.d math.d util.d alloc.d external/xxhash/xxhash.d -P-I/usr/include/freetype2 -L-lfreetype --main --unittest --of=$name
|
||||||
|
rm $name.o
|
||||||
|
./$name
|
||||||
|
rm $name
|
||||||
|
|
||||||
6
util.d
6
util.d
@ -72,19 +72,19 @@ Log(char* str)
|
|||||||
writeln(str);
|
writeln(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64
|
@nogc u64
|
||||||
KB(u64 v)
|
KB(u64 v)
|
||||||
{
|
{
|
||||||
return v * 1024;
|
return v * 1024;
|
||||||
};
|
};
|
||||||
|
|
||||||
u64
|
@nogc u64
|
||||||
MB(u64 v)
|
MB(u64 v)
|
||||||
{
|
{
|
||||||
return KB(v) * 1024;
|
return KB(v) * 1024;
|
||||||
};
|
};
|
||||||
|
|
||||||
u64
|
@nogc u64
|
||||||
GB(u64 v)
|
GB(u64 v)
|
||||||
{
|
{
|
||||||
return MB(v) * 1024;
|
return MB(v) * 1024;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user