don't need ext, handled by listen gem
This commit is contained in:
parent
5d1961ba86
commit
313f6f8214
|
@ -1,61 +0,0 @@
|
||||||
# Workaround to make Rubygems believe it builds a native gem
|
|
||||||
require 'mkmf'
|
|
||||||
create_makefile('none')
|
|
||||||
|
|
||||||
# TODO: determine whether we really need to be working around instead of with mkmf
|
|
||||||
|
|
||||||
if `uname -s`.chomp != 'Darwin'
|
|
||||||
#puts "Warning! Only Darwin (Mac OS X) systems are supported, nothing will be compiled"
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
xcode_path = %x[xcode-select -print-path].to_s.strip!
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
end
|
|
||||||
|
|
||||||
raise "Could not find a suitable Xcode installation" unless xcode_path
|
|
||||||
|
|
||||||
gem_root = File.expand_path(File.join('..'))
|
|
||||||
darwin_version = `uname -r`.to_i
|
|
||||||
sdk_version = { 9 => '10.5', 10 => '10.6', 11 => '10.7' }[darwin_version]
|
|
||||||
|
|
||||||
raise "Only Darwin systems greater than 8 (Mac OS X 10.5+) are supported" unless sdk_version
|
|
||||||
|
|
||||||
core_flags = %W{
|
|
||||||
-isysroot #{xcode_path}/SDKs/MacOSX#{sdk_version}.sdk
|
|
||||||
-mmacosx-version-min=#{sdk_version} -mdynamic-no-pic -std=gnu99
|
|
||||||
}
|
|
||||||
|
|
||||||
cflags = core_flags + %w{-Os -pipe}
|
|
||||||
|
|
||||||
wflags = %w{
|
|
||||||
-Wmissing-prototypes -Wreturn-type -Wmissing-braces -Wparentheses -Wswitch
|
|
||||||
-Wunused-function -Wunused-label -Wunused-parameter -Wunused-variable
|
|
||||||
-Wunused-value -Wuninitialized -Wunknown-pragmas -Wshadow
|
|
||||||
-Wfour-char-constants -Wsign-compare -Wnewline-eof -Wconversion
|
|
||||||
-Wshorten-64-to-32 -Wglobal-constructors -pedantic
|
|
||||||
}
|
|
||||||
|
|
||||||
ldflags = %w{
|
|
||||||
-dead_strip -framework CoreServices
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_opts = core_flags + ldflags
|
|
||||||
|
|
||||||
cc_opts += %w{
|
|
||||||
-D DEBUG=true
|
|
||||||
} if ENV['FWDEBUG'] == "true"
|
|
||||||
|
|
||||||
cc_bin = `which clang || which gcc`.to_s.strip!
|
|
||||||
|
|
||||||
compile_command = "CFLAGS='#{cflags.join(' ')} #{wflags.join(' ')}' #{cc_bin} #{cc_opts.join(' ')} -o '#{gem_root}/bin/fsevent_watch_mm' fsevent/fsevent_watch.c"
|
|
||||||
|
|
||||||
STDERR.puts(compile_command)
|
|
||||||
|
|
||||||
# Compile the actual fsevent_watch binary
|
|
||||||
system "mkdir -p #{File.join(gem_root, 'bin')}"
|
|
||||||
system compile_command
|
|
||||||
|
|
||||||
unless File.executable?("#{gem_root}/bin/fsevent_watch_mm")
|
|
||||||
raise "Compilation of fsevent_watch failed (see README)"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,226 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <CoreServices/CoreServices.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Structure for storing metadata parsed from the commandline
|
|
||||||
static struct {
|
|
||||||
FSEventStreamEventId sinceWhen;
|
|
||||||
CFTimeInterval latency;
|
|
||||||
FSEventStreamCreateFlags flags;
|
|
||||||
CFMutableArrayRef paths;
|
|
||||||
} config = {
|
|
||||||
(UInt64) kFSEventStreamEventIdSinceNow,
|
|
||||||
(double) 0.3,
|
|
||||||
(UInt32) kFSEventStreamCreateFlagNone,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
// Prototypes
|
|
||||||
static void append_path(const char *path);
|
|
||||||
static inline void parse_cli_settings(int argc, const char *argv[]);
|
|
||||||
static void callback(FSEventStreamRef streamRef,
|
|
||||||
void *clientCallBackInfo,
|
|
||||||
size_t numEvents,
|
|
||||||
void *eventPaths,
|
|
||||||
const FSEventStreamEventFlags eventFlags[],
|
|
||||||
const FSEventStreamEventId eventIds[]);
|
|
||||||
|
|
||||||
|
|
||||||
// Resolve a path and append it to the CLI settings structure
|
|
||||||
// The FSEvents API will, internally, resolve paths using a similar scheme.
|
|
||||||
// Performing this ahead of time makes things less confusing, IMHO.
|
|
||||||
static void append_path(const char *path)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "append_path called for: %s\n", path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char fullPath[PATH_MAX];
|
|
||||||
|
|
||||||
if (realpath(path, fullPath) == NULL) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, " realpath not directly resolvable from path\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (path[0] != '/') {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, " passed path is not absolute\n");
|
|
||||||
#endif
|
|
||||||
size_t len;
|
|
||||||
getcwd(fullPath, sizeof(fullPath));
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, " result of getcwd: %s\n", fullPath);
|
|
||||||
#endif
|
|
||||||
len = strlen(fullPath);
|
|
||||||
fullPath[len] = '/';
|
|
||||||
strlcpy(&fullPath[len + 1], path, sizeof(fullPath) - (len + 1));
|
|
||||||
} else {
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, " assuming path does not YET exist\n");
|
|
||||||
#endif
|
|
||||||
strlcpy(fullPath, path, sizeof(fullPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, " resolved path to: %s\n", fullPath);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault,
|
|
||||||
fullPath,
|
|
||||||
kCFStringEncodingUTF8);
|
|
||||||
CFArrayAppendValue(config.paths, pathRef);
|
|
||||||
CFRelease(pathRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse commandline settings
|
|
||||||
static inline void parse_cli_settings(int argc, const char *argv[])
|
|
||||||
{
|
|
||||||
config.paths = CFArrayCreateMutable(NULL,
|
|
||||||
(CFIndex)0,
|
|
||||||
&kCFTypeArrayCallBacks);
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
if (strcmp(argv[i], "--since-when") == 0) {
|
|
||||||
config.sinceWhen = strtoull(argv[++i], NULL, 0);
|
|
||||||
} else if (strcmp(argv[i], "--latency") == 0) {
|
|
||||||
config.latency = strtod(argv[++i], NULL);
|
|
||||||
} else if (strcmp(argv[i], "--no-defer") == 0) {
|
|
||||||
config.flags |= kFSEventStreamCreateFlagNoDefer;
|
|
||||||
} else if (strcmp(argv[i], "--watch-root") == 0) {
|
|
||||||
config.flags |= kFSEventStreamCreateFlagWatchRoot;
|
|
||||||
} else if (strcmp(argv[i], "--ignore-self") == 0) {
|
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
||||||
config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
|
|
||||||
#else
|
|
||||||
fprintf(stderr, "MacOSX10.6.sdk is required for --ignore-self\n");
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
append_path(argv[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CFArrayGetCount(config.paths) == 0) {
|
|
||||||
append_path(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen);
|
|
||||||
fprintf(stderr, "config.latency %f\n", config.latency);
|
|
||||||
fprintf(stderr, "config.flags %#.8x\n", config.flags);
|
|
||||||
fprintf(stderr, "config.paths\n");
|
|
||||||
|
|
||||||
long numpaths = CFArrayGetCount(config.paths);
|
|
||||||
|
|
||||||
for (long i = 0; i < numpaths; i++) {
|
|
||||||
char path[PATH_MAX];
|
|
||||||
CFStringGetCString(CFArrayGetValueAtIndex(config.paths, i),
|
|
||||||
path,
|
|
||||||
PATH_MAX,
|
|
||||||
kCFStringEncodingUTF8);
|
|
||||||
fprintf(stderr, " %s\n", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callback(FSEventStreamRef streamRef,
|
|
||||||
void *clientCallBackInfo,
|
|
||||||
size_t numEvents,
|
|
||||||
void *eventPaths,
|
|
||||||
const FSEventStreamEventFlags eventFlags[],
|
|
||||||
const FSEventStreamEventId eventIds[])
|
|
||||||
{
|
|
||||||
char **paths = eventPaths;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "FSEventStreamCallback fired!\n");
|
|
||||||
fprintf(stderr, " numEvents: %lu\n", numEvents);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numEvents; i++) {
|
|
||||||
fprintf(stderr, " event path: %s\n", paths[i]);
|
|
||||||
fprintf(stderr, " event flags: %#.8x\n", eventFlags[i]);
|
|
||||||
fprintf(stderr, " event ID: %llu\n", eventIds[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numEvents; i++) {
|
|
||||||
fprintf(stdout, "%s", paths[i]);
|
|
||||||
fprintf(stdout, ":");
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stdout, "\n");
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* a subprocess will initially inherit the process group of its parent. the
|
|
||||||
* process group may have a control terminal associated with it, which would
|
|
||||||
* be the first tty device opened by the group leader. typically the group
|
|
||||||
* leader is your shell and the control terminal is your login device. a
|
|
||||||
* subset of signals triggered on the control terminal are sent to all members
|
|
||||||
* of the process group, in large part to facilitate sane and consistent
|
|
||||||
* cleanup (ex: control terminal was closed).
|
|
||||||
*
|
|
||||||
* so why the overly descriptive lecture style comment?
|
|
||||||
* 1. SIGINT and SIGQUIT are among the signals with this behavior
|
|
||||||
* 2. a number of applications gank the above for their own use
|
|
||||||
* 3. ruby's insanely useful "guard" is one of these applications
|
|
||||||
* 4. despite having some level of understanding of POSIX signals and a few
|
|
||||||
* of the scenarios that might cause problems, i learned this one only
|
|
||||||
* after reading ruby 1.9's process.c
|
|
||||||
* 5. if left completely undocumented, even slightly obscure bugfixes
|
|
||||||
* may be removed as cruft by a future maintainer
|
|
||||||
*
|
|
||||||
* hindsight is 20/20 addition: if you're single-threaded and blocking on IO
|
|
||||||
* with a subprocess, then handlers for deferrable signals might not get run
|
|
||||||
* when you expect them to. In the case of Ruby 1.8, that means making use of
|
|
||||||
* IO::select, which will preserve correct signal handling behavior.
|
|
||||||
*/
|
|
||||||
if (setpgid(0,0) < 0) {
|
|
||||||
fprintf(stderr, "Unable to set new process group.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_cli_settings(argc, argv);
|
|
||||||
|
|
||||||
FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
|
|
||||||
FSEventStreamRef stream;
|
|
||||||
stream = FSEventStreamCreate(kCFAllocatorDefault,
|
|
||||||
(FSEventStreamCallback)&callback,
|
|
||||||
&context,
|
|
||||||
config.paths,
|
|
||||||
config.sinceWhen,
|
|
||||||
config.latency,
|
|
||||||
config.flags);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
FSEventStreamShow(stream);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FSEventStreamScheduleWithRunLoop(stream,
|
|
||||||
CFRunLoopGetCurrent(),
|
|
||||||
kCFRunLoopDefaultMode);
|
|
||||||
FSEventStreamStart(stream);
|
|
||||||
CFRunLoopRun();
|
|
||||||
FSEventStreamFlushSync(stream);
|
|
||||||
FSEventStreamStop(stream);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in a new issue