frank_afc_proxy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/FrankAfcProxy.xcodeproj/project.pbxproj +260 -0
- data/FrankAfcProxy/FileChangeObserver.h +52 -0
- data/FrankAfcProxy/FileChangeObserver.m +160 -0
- data/FrankAfcProxy/FrankAfcProxy-Prefix.pch +7 -0
- data/FrankAfcProxy/FrankAfcProxy.h +14 -0
- data/FrankAfcProxy/FrankAfcProxy.m +137 -0
- data/FrankAfcProxy/FrankAfcProxyLoader.h +12 -0
- data/FrankAfcProxy/FrankAfcProxyLoader.m +30 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +60 -0
- data/bin/frankAfcProxy +203 -0
- data/bin/frankAfcProxy-install +31 -0
- data/frankAfcProxy-skeleton/libFrankAfcProxy.a +0 -0
- data/frank_afc_proxy.gemspec +26 -0
- data/lib/frank_afc_proxy.rb +5 -0
- data/lib/frank_afc_proxy/version.rb +3 -0
- metadata +153 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Xcode
|
|
2
|
+
.DS_Store
|
|
3
|
+
build/
|
|
4
|
+
*.pbxuser
|
|
5
|
+
!default.pbxuser
|
|
6
|
+
*.mode1v3
|
|
7
|
+
!default.mode1v3
|
|
8
|
+
*.mode2v3
|
|
9
|
+
!default.mode2v3
|
|
10
|
+
*.perspectivev3
|
|
11
|
+
!default.perspectivev3
|
|
12
|
+
*.xcworkspace
|
|
13
|
+
!default.xcworkspace
|
|
14
|
+
xcuserdata
|
|
15
|
+
profile
|
|
16
|
+
*.moved-aside
|
|
17
|
+
DerivedData
|
|
18
|
+
.idea/
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// !$*UTF8*$!
|
|
2
|
+
{
|
|
3
|
+
archiveVersion = 1;
|
|
4
|
+
classes = {
|
|
5
|
+
};
|
|
6
|
+
objectVersion = 46;
|
|
7
|
+
objects = {
|
|
8
|
+
|
|
9
|
+
/* Begin PBXBuildFile section */
|
|
10
|
+
524888DF177B0C9B0032EEA1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 524888DE177B0C9B0032EEA1 /* Foundation.framework */; };
|
|
11
|
+
524888E4177B0C9B0032EEA1 /* FrankAfcProxy.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 524888E3177B0C9B0032EEA1 /* FrankAfcProxy.h */; };
|
|
12
|
+
524888E6177B0C9B0032EEA1 /* FrankAfcProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 524888E5177B0C9B0032EEA1 /* FrankAfcProxy.m */; };
|
|
13
|
+
524888F0177B0D930032EEA1 /* FileChangeObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 524888ED177B0D930032EEA1 /* FileChangeObserver.m */; };
|
|
14
|
+
524888F1177B0D930032EEA1 /* FrankAfcProxyLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 524888EF177B0D930032EEA1 /* FrankAfcProxyLoader.m */; };
|
|
15
|
+
/* End PBXBuildFile section */
|
|
16
|
+
|
|
17
|
+
/* Begin PBXCopyFilesBuildPhase section */
|
|
18
|
+
524888D9177B0C9B0032EEA1 /* CopyFiles */ = {
|
|
19
|
+
isa = PBXCopyFilesBuildPhase;
|
|
20
|
+
buildActionMask = 2147483647;
|
|
21
|
+
dstPath = "include/${PRODUCT_NAME}";
|
|
22
|
+
dstSubfolderSpec = 16;
|
|
23
|
+
files = (
|
|
24
|
+
524888E4177B0C9B0032EEA1 /* FrankAfcProxy.h in CopyFiles */,
|
|
25
|
+
);
|
|
26
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
27
|
+
};
|
|
28
|
+
/* End PBXCopyFilesBuildPhase section */
|
|
29
|
+
|
|
30
|
+
/* Begin PBXFileReference section */
|
|
31
|
+
524888DB177B0C9B0032EEA1 /* libFrankAfcProxy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFrankAfcProxy.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
32
|
+
524888DE177B0C9B0032EEA1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
|
33
|
+
524888E2177B0C9B0032EEA1 /* FrankAfcProxy-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FrankAfcProxy-Prefix.pch"; sourceTree = "<group>"; };
|
|
34
|
+
524888E3177B0C9B0032EEA1 /* FrankAfcProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrankAfcProxy.h; sourceTree = "<group>"; };
|
|
35
|
+
524888E5177B0C9B0032EEA1 /* FrankAfcProxy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrankAfcProxy.m; sourceTree = "<group>"; };
|
|
36
|
+
524888EC177B0D930032EEA1 /* FileChangeObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileChangeObserver.h; sourceTree = "<group>"; };
|
|
37
|
+
524888ED177B0D930032EEA1 /* FileChangeObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FileChangeObserver.m; sourceTree = "<group>"; };
|
|
38
|
+
524888EE177B0D930032EEA1 /* FrankAfcProxyLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FrankAfcProxyLoader.h; sourceTree = "<group>"; };
|
|
39
|
+
524888EF177B0D930032EEA1 /* FrankAfcProxyLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FrankAfcProxyLoader.m; sourceTree = "<group>"; };
|
|
40
|
+
/* End PBXFileReference section */
|
|
41
|
+
|
|
42
|
+
/* Begin PBXFrameworksBuildPhase section */
|
|
43
|
+
524888D8177B0C9B0032EEA1 /* Frameworks */ = {
|
|
44
|
+
isa = PBXFrameworksBuildPhase;
|
|
45
|
+
buildActionMask = 2147483647;
|
|
46
|
+
files = (
|
|
47
|
+
524888DF177B0C9B0032EEA1 /* Foundation.framework in Frameworks */,
|
|
48
|
+
);
|
|
49
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
50
|
+
};
|
|
51
|
+
/* End PBXFrameworksBuildPhase section */
|
|
52
|
+
|
|
53
|
+
/* Begin PBXGroup section */
|
|
54
|
+
524888D2177B0C9B0032EEA1 = {
|
|
55
|
+
isa = PBXGroup;
|
|
56
|
+
children = (
|
|
57
|
+
524888E0177B0C9B0032EEA1 /* FrankAfcProxy */,
|
|
58
|
+
524888DD177B0C9B0032EEA1 /* Frameworks */,
|
|
59
|
+
524888DC177B0C9B0032EEA1 /* Products */,
|
|
60
|
+
);
|
|
61
|
+
sourceTree = "<group>";
|
|
62
|
+
};
|
|
63
|
+
524888DC177B0C9B0032EEA1 /* Products */ = {
|
|
64
|
+
isa = PBXGroup;
|
|
65
|
+
children = (
|
|
66
|
+
524888DB177B0C9B0032EEA1 /* libFrankAfcProxy.a */,
|
|
67
|
+
);
|
|
68
|
+
name = Products;
|
|
69
|
+
sourceTree = "<group>";
|
|
70
|
+
};
|
|
71
|
+
524888DD177B0C9B0032EEA1 /* Frameworks */ = {
|
|
72
|
+
isa = PBXGroup;
|
|
73
|
+
children = (
|
|
74
|
+
524888DE177B0C9B0032EEA1 /* Foundation.framework */,
|
|
75
|
+
);
|
|
76
|
+
name = Frameworks;
|
|
77
|
+
sourceTree = "<group>";
|
|
78
|
+
};
|
|
79
|
+
524888E0177B0C9B0032EEA1 /* FrankAfcProxy */ = {
|
|
80
|
+
isa = PBXGroup;
|
|
81
|
+
children = (
|
|
82
|
+
524888EC177B0D930032EEA1 /* FileChangeObserver.h */,
|
|
83
|
+
524888ED177B0D930032EEA1 /* FileChangeObserver.m */,
|
|
84
|
+
524888EE177B0D930032EEA1 /* FrankAfcProxyLoader.h */,
|
|
85
|
+
524888EF177B0D930032EEA1 /* FrankAfcProxyLoader.m */,
|
|
86
|
+
524888E3177B0C9B0032EEA1 /* FrankAfcProxy.h */,
|
|
87
|
+
524888E5177B0C9B0032EEA1 /* FrankAfcProxy.m */,
|
|
88
|
+
524888E1177B0C9B0032EEA1 /* Supporting Files */,
|
|
89
|
+
);
|
|
90
|
+
path = FrankAfcProxy;
|
|
91
|
+
sourceTree = "<group>";
|
|
92
|
+
};
|
|
93
|
+
524888E1177B0C9B0032EEA1 /* Supporting Files */ = {
|
|
94
|
+
isa = PBXGroup;
|
|
95
|
+
children = (
|
|
96
|
+
524888E2177B0C9B0032EEA1 /* FrankAfcProxy-Prefix.pch */,
|
|
97
|
+
);
|
|
98
|
+
name = "Supporting Files";
|
|
99
|
+
sourceTree = "<group>";
|
|
100
|
+
};
|
|
101
|
+
/* End PBXGroup section */
|
|
102
|
+
|
|
103
|
+
/* Begin PBXNativeTarget section */
|
|
104
|
+
524888DA177B0C9B0032EEA1 /* FrankAfcProxy */ = {
|
|
105
|
+
isa = PBXNativeTarget;
|
|
106
|
+
buildConfigurationList = 524888E9177B0C9B0032EEA1 /* Build configuration list for PBXNativeTarget "FrankAfcProxy" */;
|
|
107
|
+
buildPhases = (
|
|
108
|
+
524888D7177B0C9B0032EEA1 /* Sources */,
|
|
109
|
+
524888D8177B0C9B0032EEA1 /* Frameworks */,
|
|
110
|
+
524888D9177B0C9B0032EEA1 /* CopyFiles */,
|
|
111
|
+
);
|
|
112
|
+
buildRules = (
|
|
113
|
+
);
|
|
114
|
+
dependencies = (
|
|
115
|
+
);
|
|
116
|
+
name = FrankAfcProxy;
|
|
117
|
+
productName = FrankAfcProxy;
|
|
118
|
+
productReference = 524888DB177B0C9B0032EEA1 /* libFrankAfcProxy.a */;
|
|
119
|
+
productType = "com.apple.product-type.library.static";
|
|
120
|
+
};
|
|
121
|
+
/* End PBXNativeTarget section */
|
|
122
|
+
|
|
123
|
+
/* Begin PBXProject section */
|
|
124
|
+
524888D3177B0C9B0032EEA1 /* Project object */ = {
|
|
125
|
+
isa = PBXProject;
|
|
126
|
+
attributes = {
|
|
127
|
+
LastUpgradeCheck = 0460;
|
|
128
|
+
ORGANIZATIONNAME = suzumura_ss;
|
|
129
|
+
};
|
|
130
|
+
buildConfigurationList = 524888D6177B0C9B0032EEA1 /* Build configuration list for PBXProject "FrankAfcProxy" */;
|
|
131
|
+
compatibilityVersion = "Xcode 3.2";
|
|
132
|
+
developmentRegion = English;
|
|
133
|
+
hasScannedForEncodings = 0;
|
|
134
|
+
knownRegions = (
|
|
135
|
+
en,
|
|
136
|
+
);
|
|
137
|
+
mainGroup = 524888D2177B0C9B0032EEA1;
|
|
138
|
+
productRefGroup = 524888DC177B0C9B0032EEA1 /* Products */;
|
|
139
|
+
projectDirPath = "";
|
|
140
|
+
projectRoot = "";
|
|
141
|
+
targets = (
|
|
142
|
+
524888DA177B0C9B0032EEA1 /* FrankAfcProxy */,
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
/* End PBXProject section */
|
|
146
|
+
|
|
147
|
+
/* Begin PBXSourcesBuildPhase section */
|
|
148
|
+
524888D7177B0C9B0032EEA1 /* Sources */ = {
|
|
149
|
+
isa = PBXSourcesBuildPhase;
|
|
150
|
+
buildActionMask = 2147483647;
|
|
151
|
+
files = (
|
|
152
|
+
524888E6177B0C9B0032EEA1 /* FrankAfcProxy.m in Sources */,
|
|
153
|
+
524888F0177B0D930032EEA1 /* FileChangeObserver.m in Sources */,
|
|
154
|
+
524888F1177B0D930032EEA1 /* FrankAfcProxyLoader.m in Sources */,
|
|
155
|
+
);
|
|
156
|
+
runOnlyForDeploymentPostprocessing = 0;
|
|
157
|
+
};
|
|
158
|
+
/* End PBXSourcesBuildPhase section */
|
|
159
|
+
|
|
160
|
+
/* Begin XCBuildConfiguration section */
|
|
161
|
+
524888E7177B0C9B0032EEA1 /* Debug */ = {
|
|
162
|
+
isa = XCBuildConfiguration;
|
|
163
|
+
buildSettings = {
|
|
164
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
165
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
166
|
+
CLANG_CXX_LIBRARY = "libc++";
|
|
167
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
168
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
169
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
170
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
171
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
172
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
173
|
+
COPY_PHASE_STRIP = NO;
|
|
174
|
+
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
175
|
+
GCC_DYNAMIC_NO_PIC = NO;
|
|
176
|
+
GCC_OPTIMIZATION_LEVEL = 0;
|
|
177
|
+
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
178
|
+
"DEBUG=1",
|
|
179
|
+
"$(inherited)",
|
|
180
|
+
);
|
|
181
|
+
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
|
182
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
|
183
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
|
184
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
185
|
+
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
|
186
|
+
ONLY_ACTIVE_ARCH = YES;
|
|
187
|
+
SDKROOT = iphoneos;
|
|
188
|
+
};
|
|
189
|
+
name = Debug;
|
|
190
|
+
};
|
|
191
|
+
524888E8177B0C9B0032EEA1 /* Release */ = {
|
|
192
|
+
isa = XCBuildConfiguration;
|
|
193
|
+
buildSettings = {
|
|
194
|
+
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
195
|
+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
|
196
|
+
CLANG_CXX_LIBRARY = "libc++";
|
|
197
|
+
CLANG_ENABLE_OBJC_ARC = YES;
|
|
198
|
+
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
199
|
+
CLANG_WARN_EMPTY_BODY = YES;
|
|
200
|
+
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
201
|
+
CLANG_WARN_INT_CONVERSION = YES;
|
|
202
|
+
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
203
|
+
COPY_PHASE_STRIP = YES;
|
|
204
|
+
GCC_C_LANGUAGE_STANDARD = gnu99;
|
|
205
|
+
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
|
206
|
+
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
|
207
|
+
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
208
|
+
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
|
|
209
|
+
SDKROOT = iphoneos;
|
|
210
|
+
VALIDATE_PRODUCT = YES;
|
|
211
|
+
};
|
|
212
|
+
name = Release;
|
|
213
|
+
};
|
|
214
|
+
524888EA177B0C9B0032EEA1 /* Debug */ = {
|
|
215
|
+
isa = XCBuildConfiguration;
|
|
216
|
+
buildSettings = {
|
|
217
|
+
DSTROOT = /tmp/FrankAfcProxy.dst;
|
|
218
|
+
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
|
219
|
+
GCC_PREFIX_HEADER = "FrankAfcProxy/FrankAfcProxy-Prefix.pch";
|
|
220
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
221
|
+
SKIP_INSTALL = YES;
|
|
222
|
+
};
|
|
223
|
+
name = Debug;
|
|
224
|
+
};
|
|
225
|
+
524888EB177B0C9B0032EEA1 /* Release */ = {
|
|
226
|
+
isa = XCBuildConfiguration;
|
|
227
|
+
buildSettings = {
|
|
228
|
+
DSTROOT = /tmp/FrankAfcProxy.dst;
|
|
229
|
+
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
|
230
|
+
GCC_PREFIX_HEADER = "FrankAfcProxy/FrankAfcProxy-Prefix.pch";
|
|
231
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
232
|
+
SKIP_INSTALL = YES;
|
|
233
|
+
};
|
|
234
|
+
name = Release;
|
|
235
|
+
};
|
|
236
|
+
/* End XCBuildConfiguration section */
|
|
237
|
+
|
|
238
|
+
/* Begin XCConfigurationList section */
|
|
239
|
+
524888D6177B0C9B0032EEA1 /* Build configuration list for PBXProject "FrankAfcProxy" */ = {
|
|
240
|
+
isa = XCConfigurationList;
|
|
241
|
+
buildConfigurations = (
|
|
242
|
+
524888E7177B0C9B0032EEA1 /* Debug */,
|
|
243
|
+
524888E8177B0C9B0032EEA1 /* Release */,
|
|
244
|
+
);
|
|
245
|
+
defaultConfigurationIsVisible = 0;
|
|
246
|
+
defaultConfigurationName = Release;
|
|
247
|
+
};
|
|
248
|
+
524888E9177B0C9B0032EEA1 /* Build configuration list for PBXNativeTarget "FrankAfcProxy" */ = {
|
|
249
|
+
isa = XCConfigurationList;
|
|
250
|
+
buildConfigurations = (
|
|
251
|
+
524888EA177B0C9B0032EEA1 /* Debug */,
|
|
252
|
+
524888EB177B0C9B0032EEA1 /* Release */,
|
|
253
|
+
);
|
|
254
|
+
defaultConfigurationIsVisible = 0;
|
|
255
|
+
defaultConfigurationName = Release;
|
|
256
|
+
};
|
|
257
|
+
/* End XCConfigurationList section */
|
|
258
|
+
};
|
|
259
|
+
rootObject = 524888D3177B0C9B0032EEA1 /* Project object */;
|
|
260
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FileChangeObserver.h
|
|
3
|
+
//
|
|
4
|
+
// Created by Toshiyuki Suzumura on 2013/06/11.
|
|
5
|
+
// Copyright (c) 2013 Toshiyuki Suzumura All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
#include <sys/types.h>
|
|
10
|
+
#include <sys/event.h>
|
|
11
|
+
#include <sys/time.h>
|
|
12
|
+
|
|
13
|
+
typedef enum {
|
|
14
|
+
kFileChangeType_Delete = NOTE_DELETE,
|
|
15
|
+
kFileChangeType_Write = NOTE_WRITE,
|
|
16
|
+
kFileChangeType_DirectoryContentsChanged = kFileChangeType_Write
|
|
17
|
+
//#define NOTE_WRITE 0x00000002 /* data contents changed */
|
|
18
|
+
//#define NOTE_EXTEND 0x00000004 /* size increased */
|
|
19
|
+
//#define NOTE_ATTRIB 0x00000008 /* attributes changed */
|
|
20
|
+
//#define NOTE_LINK 0x00000010 /* link count changed */
|
|
21
|
+
//#define NOTE_RENAME 0x00000020 /* vnode was renamed */
|
|
22
|
+
//#define NOTE_REVOKE 0x00000040 /* vnode access was revoked */
|
|
23
|
+
//#define NOTE_NONE 0x00000080 /* No specific vnode event: to test for EVFILT_READ activation*/
|
|
24
|
+
} FileChangeNotificationType;
|
|
25
|
+
|
|
26
|
+
@class FileChangeObserver;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@protocol FileChangeObserverDelegate<NSObject>
|
|
30
|
+
|
|
31
|
+
@required
|
|
32
|
+
- (void)fileChanged:(FileChangeObserver*)observer typeMask:(FileChangeNotificationType)type;
|
|
33
|
+
|
|
34
|
+
@end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@interface FileChangeObserver : NSObject
|
|
38
|
+
|
|
39
|
+
@property (nonatomic, copy) NSURL * url;
|
|
40
|
+
@property (nonatomic, weak) id<FileChangeObserverDelegate> delegate;
|
|
41
|
+
|
|
42
|
+
+ (instancetype)observerForURL:(NSURL*)url types:(FileChangeNotificationType)types delegate:(id<FileChangeObserverDelegate>)delegate;
|
|
43
|
+
- (void)invalidate;
|
|
44
|
+
|
|
45
|
+
@end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@interface NSString (FileChangeObserver)
|
|
49
|
+
|
|
50
|
+
+ (NSString*)stringWithKEventFFlags:(FileChangeNotificationType)flags;
|
|
51
|
+
|
|
52
|
+
@end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FileChangeObserver.m
|
|
3
|
+
//
|
|
4
|
+
// Created by Toshiyuki Suzumura on 2013/06/11.
|
|
5
|
+
// Copyright (c) 2013 Toshiyuki Suzumura All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "FileChangeObserver.h"
|
|
9
|
+
#include <sys/stat.h>
|
|
10
|
+
|
|
11
|
+
#undef Assert
|
|
12
|
+
#define Assert(COND) { if (!(COND)) { raise( SIGINT ) ; } }
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@interface FileChangeObserver ()
|
|
16
|
+
|
|
17
|
+
@property (nonatomic, readonly) int kqueue;
|
|
18
|
+
@property (nonatomic) FileChangeNotificationType typeMask;
|
|
19
|
+
|
|
20
|
+
@end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@implementation FileChangeObserver
|
|
25
|
+
@synthesize kqueue = _kqueue ;
|
|
26
|
+
|
|
27
|
+
+ (instancetype)observerForURL:(NSURL*)url types:(FileChangeNotificationType)types delegate:(id<FileChangeObserverDelegate>)delegate
|
|
28
|
+
{
|
|
29
|
+
if (!url) { return nil; }
|
|
30
|
+
|
|
31
|
+
FileChangeObserver * result = [[[self class] alloc] init];
|
|
32
|
+
result.url = url;
|
|
33
|
+
result.delegate = delegate;
|
|
34
|
+
result.typeMask = types;
|
|
35
|
+
|
|
36
|
+
[result startObserving];
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
-(void)dealloc
|
|
41
|
+
{
|
|
42
|
+
[self stopObserving];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
//
|
|
47
|
+
// kqueue_main
|
|
48
|
+
//
|
|
49
|
+
static void (^kqueue_main)(FileChangeObserver *) = ^(__unsafe_unretained FileChangeObserver* self)
|
|
50
|
+
{
|
|
51
|
+
while (true) {
|
|
52
|
+
int fd = open([[self.url path] fileSystemRepresentation], O_EVTONLY);
|
|
53
|
+
if (fd<0) {
|
|
54
|
+
usleep(100);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
int q = self.kqueue;
|
|
59
|
+
{
|
|
60
|
+
struct kevent event = {
|
|
61
|
+
.ident = fd,
|
|
62
|
+
.filter = EVFILT_VNODE,
|
|
63
|
+
.flags = EV_ADD | EV_CLEAR,
|
|
64
|
+
.fflags = self.typeMask,
|
|
65
|
+
};
|
|
66
|
+
int error = kevent(q, &event, 1, NULL, 0, NULL);
|
|
67
|
+
Assert(error == 0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
struct kevent event = {0};
|
|
71
|
+
while (true) {
|
|
72
|
+
int nEvents = kevent(q, NULL, 0, &event, 1, NULL);
|
|
73
|
+
if (nEvents!=1) {
|
|
74
|
+
NSLog(@"kevent() failed - %s", strerror(errno));
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
__block FileChangeNotificationType e = event.fflags;
|
|
78
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
79
|
+
[self.delegate fileChanged:self typeMask:e];
|
|
80
|
+
});
|
|
81
|
+
if (e & NOTE_DELETE) {
|
|
82
|
+
NSLog(@"DELETE detected.");
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
close(fd);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
-(void)stopObserving
|
|
92
|
+
{
|
|
93
|
+
@synchronized(self)
|
|
94
|
+
{
|
|
95
|
+
close(self.kqueue);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
-(void)invalidate
|
|
100
|
+
{
|
|
101
|
+
[self stopObserving];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
-(void)startObserving
|
|
105
|
+
{
|
|
106
|
+
@synchronized(self)
|
|
107
|
+
{
|
|
108
|
+
static dispatch_queue_t __q ;
|
|
109
|
+
static dispatch_once_t onceToken;
|
|
110
|
+
dispatch_once(&onceToken, ^{
|
|
111
|
+
__q = dispatch_queue_create("file observer queue", DISPATCH_QUEUE_CONCURRENT);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// this is __unsafe_unretained to avoid retaining 'self'.
|
|
115
|
+
// using __weak isn't a strong enough to prevent ARC from retaining 'self'.
|
|
116
|
+
// This allows the observer to be torn down simply be releasing it,
|
|
117
|
+
// instead of requiring the client to invoke -invalidate.
|
|
118
|
+
__unsafe_unretained id unsafe_self = self ;
|
|
119
|
+
dispatch_async( __q, ^{
|
|
120
|
+
kqueue_main(unsafe_self);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
-(int)kqueue
|
|
126
|
+
{
|
|
127
|
+
if (!_kqueue) { _kqueue = kqueue(); }
|
|
128
|
+
return _kqueue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@implementation NSString (FileChangeObserver)
|
|
135
|
+
|
|
136
|
+
+ (NSString*)stringWithKEventFFlags:(FileChangeNotificationType)flags
|
|
137
|
+
{
|
|
138
|
+
NSMutableArray * array = [NSMutableArray array];
|
|
139
|
+
static const struct {
|
|
140
|
+
const char* name;
|
|
141
|
+
} bitNames[] = {
|
|
142
|
+
{ "NOTE_DELETE" },
|
|
143
|
+
{ "NOTE_WRITE" },
|
|
144
|
+
{ "NOTE_EXTEND" },
|
|
145
|
+
{ "NOTE_ATTRIB" },
|
|
146
|
+
{ "NOTE_LINK" },
|
|
147
|
+
{ "NOTE_RENAME" },
|
|
148
|
+
{ "NOTE_REVOKE" },
|
|
149
|
+
{ "NOTE_NONE" }
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
for(int index=0, count=sizeof(bitNames)/sizeof(bitNames[0]); index<count; ++index) {
|
|
153
|
+
if ((flags&(1<<index))!=0) {
|
|
154
|
+
[array addObject:[NSString stringWithCString:bitNames[index].name encoding:NSUTF8StringEncoding]];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return [array componentsJoinedByString:@" "];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FrankAfcProxy.h
|
|
3
|
+
//
|
|
4
|
+
// Created by Toshiyuki Suzumura on 2013/06/11.
|
|
5
|
+
// Copyright (c) 2013 Toshiyuki Suzumura All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
|
|
10
|
+
@interface FrankAfcProxy : NSObject
|
|
11
|
+
|
|
12
|
+
+ (void)run;
|
|
13
|
+
|
|
14
|
+
@end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FrankAfcProxy.m
|
|
3
|
+
//
|
|
4
|
+
// Created by Toshiyuki Suzumura on 2013/06/11.
|
|
5
|
+
// Copyright (c) 2013 Toshiyuki Suzumura All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "FrankAfcProxy.h"
|
|
9
|
+
#import "FileChangeObserver.h"
|
|
10
|
+
|
|
11
|
+
#ifndef FRANKAFCPROXY_PRODUCT_VERSION
|
|
12
|
+
#define FRANKAFCPROXY_PRODUCT_VERSION UNKNOWN
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#define xstr(s) str(s)
|
|
16
|
+
#define str(s) #s
|
|
17
|
+
#define VERSIONED_NAME "FrankAfcProxy iOS Server " xstr(FRANKAFCPROXY_PRODUCT_VERSION)
|
|
18
|
+
const unsigned char frankAfcProxy_version[] = "@(#)" VERSIONED_NAME "\n";
|
|
19
|
+
|
|
20
|
+
@interface FrankAfcProxy () <FileChangeObserverDelegate>
|
|
21
|
+
{
|
|
22
|
+
NSOperationQueue* _queueHTTPRequest;
|
|
23
|
+
FileChangeObserver* _header_observer;
|
|
24
|
+
NSString* _in_header;
|
|
25
|
+
NSString* _in_body;
|
|
26
|
+
NSString* _out_header;
|
|
27
|
+
NSString* _out_body;
|
|
28
|
+
}
|
|
29
|
+
@end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@implementation FrankAfcProxy
|
|
33
|
+
|
|
34
|
+
- (void)httpRequestWithHeaderData:(NSData*)headerData bodyData:(NSData*)bodyData
|
|
35
|
+
{
|
|
36
|
+
if (!headerData) {
|
|
37
|
+
[@"Bad request: header is nil.\n" writeToFile:_out_body atomically:NO encoding:NSUTF8StringEncoding error:nil];
|
|
38
|
+
[@{@"Status": @400} writeToFile:_out_header atomically:NO];
|
|
39
|
+
NSLog(@"header is nil");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
NSMutableDictionary* headers = [NSJSONSerialization JSONObjectWithData:headerData
|
|
44
|
+
options:NSJSONReadingMutableContainers
|
|
45
|
+
error:nil];
|
|
46
|
+
|
|
47
|
+
NSMutableURLRequest* req = [[NSMutableURLRequest alloc] init];
|
|
48
|
+
[req setURL:[NSURL URLWithString:[headers objectForKey:@"URI"]]];
|
|
49
|
+
[req setHTTPMethod:[headers objectForKey:@"Method"]];
|
|
50
|
+
[headers removeObjectForKey:@"URI"];
|
|
51
|
+
[headers removeObjectForKey:@"Method"];
|
|
52
|
+
|
|
53
|
+
for (NSString* key in headers.keyEnumerator) {
|
|
54
|
+
[req setValue:[headers objectForKey:key] forHTTPHeaderField:key];
|
|
55
|
+
}
|
|
56
|
+
[req setHTTPBody:bodyData];
|
|
57
|
+
|
|
58
|
+
NSLog(@"\n%@ %@\n%@\n%@", req.HTTPMethod, req.URL.description, req.allHTTPHeaderFields.description, req.HTTPBody.description);
|
|
59
|
+
|
|
60
|
+
[NSURLConnection sendAsynchronousRequest:req
|
|
61
|
+
queue:_queueHTTPRequest
|
|
62
|
+
completionHandler:^(NSURLResponse* res_, NSData* response_body, NSError* response_error) {
|
|
63
|
+
NSHTTPURLResponse* res = (NSHTTPURLResponse*)res_;
|
|
64
|
+
NSLog(@"=> %d", res.statusCode);
|
|
65
|
+
|
|
66
|
+
[response_body writeToFile:_out_body atomically:NO];
|
|
67
|
+
NSMutableDictionary* result_headers = [NSMutableDictionary dictionaryWithDictionary:res.allHeaderFields];
|
|
68
|
+
[result_headers setObject:[NSNumber numberWithInteger:res.statusCode]
|
|
69
|
+
forKey:@"Status"];
|
|
70
|
+
NSData* header = [NSJSONSerialization dataWithJSONObject:result_headers
|
|
71
|
+
options:0
|
|
72
|
+
error:nil];
|
|
73
|
+
[header writeToFile:_out_header atomically:NO];
|
|
74
|
+
NSLog(@"%@ => %d", req.URL, header.length);
|
|
75
|
+
}];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
#pragma mark - FileChangeObserverDelegate
|
|
81
|
+
|
|
82
|
+
- (void)fileChanged:(FileChangeObserver*)observer typeMask:(FileChangeNotificationType)type
|
|
83
|
+
{
|
|
84
|
+
NSLog(@"header in: %@:", [NSString stringWithKEventFFlags:type]);
|
|
85
|
+
if (type & NOTE_DELETE) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
NSData* headerData = [NSData dataWithContentsOfFile:_in_header];
|
|
89
|
+
NSData* bodyData = [NSData dataWithContentsOfFile:_in_body];
|
|
90
|
+
truncate(_in_header.UTF8String, 0);
|
|
91
|
+
truncate(_in_body.UTF8String, 0);
|
|
92
|
+
[self httpRequestWithHeaderData:headerData bodyData:bodyData];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
#pragma mark - Life cycle.
|
|
98
|
+
|
|
99
|
+
+ (void)run
|
|
100
|
+
{
|
|
101
|
+
static FrankAfcProxy* proxy;
|
|
102
|
+
static dispatch_once_t onceToken;
|
|
103
|
+
dispatch_once(&onceToken, ^{
|
|
104
|
+
proxy = [[FrankAfcProxy alloc] init];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
- (id)init
|
|
109
|
+
{
|
|
110
|
+
self = [super init];
|
|
111
|
+
if (self) {
|
|
112
|
+
_queueHTTPRequest = [[NSOperationQueue alloc] init];
|
|
113
|
+
_queueHTTPRequest.maxConcurrentOperationCount = 1;
|
|
114
|
+
|
|
115
|
+
NSError* err;
|
|
116
|
+
NSString* dir = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
|
|
117
|
+
_in_header = [dir stringByAppendingPathComponent:@"in.header"];
|
|
118
|
+
_in_body = [dir stringByAppendingPathComponent:@"in.body"];
|
|
119
|
+
_out_header = [dir stringByAppendingPathComponent:@"out.header"];
|
|
120
|
+
_out_body = [dir stringByAppendingPathComponent:@"out.body"];
|
|
121
|
+
|
|
122
|
+
NSFileManager* fm = [NSFileManager defaultManager];
|
|
123
|
+
[fm createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&err];
|
|
124
|
+
[fm createFileAtPath:_in_header contents:nil attributes:nil];
|
|
125
|
+
[fm createFileAtPath:_in_body contents:nil attributes:nil];
|
|
126
|
+
[fm createFileAtPath:_out_header contents:nil attributes:nil];
|
|
127
|
+
[fm createFileAtPath:_out_body contents:nil attributes:nil];
|
|
128
|
+
|
|
129
|
+
_header_observer = [FileChangeObserver observerForURL:[[NSURL alloc] initFileURLWithPath:_in_header]
|
|
130
|
+
types:kFileChangeType_Delete|kFileChangeType_Write
|
|
131
|
+
delegate:self];
|
|
132
|
+
NSLog(@"Target: %@/\n", dir);
|
|
133
|
+
}
|
|
134
|
+
return self;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//
|
|
2
|
+
// FrankAfcProxyLoader.m
|
|
3
|
+
//
|
|
4
|
+
// Created by Toshiyuki Terashita on 2013/06/26.
|
|
5
|
+
// Copyright (c) 2013 Toshiyuki Suzumura. All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "FrankAfcProxyLoader.h"
|
|
9
|
+
#import "FrankAfcProxy.h"
|
|
10
|
+
|
|
11
|
+
@implementation FrankAfcProxyLoader
|
|
12
|
+
|
|
13
|
+
+ (void)applicationDidBecomeActive:(NSNotification*)notification
|
|
14
|
+
{
|
|
15
|
+
[FrankAfcProxy run];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
+ (void)load
|
|
19
|
+
{
|
|
20
|
+
NSLog(@"Injecting FrankAfcProxy");
|
|
21
|
+
|
|
22
|
+
NSString *notificationName = @"UIApplicationDidBecomeActiveNotification";
|
|
23
|
+
|
|
24
|
+
[[NSNotificationCenter defaultCenter] addObserver:[self class]
|
|
25
|
+
selector:@selector(applicationDidBecomeActive:)
|
|
26
|
+
name:notificationName
|
|
27
|
+
object:nil];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@end
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Toshiyuki Suzumura
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# FrankAfcProxy
|
|
2
|
+
|
|
3
|
+
Connect to FrankServer with iPhone USB cable via afc.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Required gem io_afc, and libimobiledevice version 1.1.5 or later.
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
gem 'frank_afc_proxy'
|
|
13
|
+
|
|
14
|
+
And then execute:
|
|
15
|
+
|
|
16
|
+
$ bundle
|
|
17
|
+
|
|
18
|
+
Or install it yourself as:
|
|
19
|
+
|
|
20
|
+
$ gem install frank_afc_proxy
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
1. First, run frank setup In your application project directory.
|
|
27
|
+
|
|
28
|
+
$ frank setup
|
|
29
|
+
|
|
30
|
+
2. And install frankAfcProxy into your application.
|
|
31
|
+
|
|
32
|
+
$ frankAfcProxy-install
|
|
33
|
+
|
|
34
|
+
3. Build and launch your application with fruitstrap.
|
|
35
|
+
|
|
36
|
+
http://www.testingwithfrank.com/device.html
|
|
37
|
+
|
|
38
|
+
4. Start frankAfcProxy with your application bundle identifer.
|
|
39
|
+
|
|
40
|
+
$ frankAfcProxy start --appid=<bundle identifer>
|
|
41
|
+
|
|
42
|
+
You can check bundle identifiers in the following ways.
|
|
43
|
+
|
|
44
|
+
$ frankAfcProxy list
|
|
45
|
+
|
|
46
|
+
5. Run frank console with --server option.
|
|
47
|
+
|
|
48
|
+
$ frank console --server=http://localhost:4000/
|
|
49
|
+
|
|
50
|
+
Or, Run test with server_base_url settings.
|
|
51
|
+
|
|
52
|
+
Frank::Cucumber::FrankHelper.server_base_url = "http://localhost:4000/"
|
|
53
|
+
|
|
54
|
+
6. Access to device with FrankHelper.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Contributing
|
|
59
|
+
|
|
60
|
+
1. Fork it
|
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
64
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require File.expand_path('../lib/frank_afc_proxy/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
def discover_latest_sdk_for(platform)
|
|
5
|
+
`xcodebuild -showsdks | grep -o "#{platform}.*$" | sort | tail -n 1`.chomp
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def build_dir
|
|
9
|
+
File.expand_path('../build', __FILE__)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build_ios_library(platform)
|
|
13
|
+
sdk = discover_latest_sdk_for(platform)
|
|
14
|
+
puts "building #{{sdk:sdk}}"
|
|
15
|
+
preprocessor_flag = %Q|GCC_PREPROCESSOR_DEFINITIONS='$(inherited) FRANKAFCPROXY_PRODUCT_VERSION=\"#{FrankAfcProxy::VERSION}\"'|
|
|
16
|
+
sh "xcodebuild -project FrankAfcProxy.xcodeproj -configuration Release -sdk #{sdk} BUILD_DIR=\"#{build_dir}\" #{preprocessor_flag} clean build"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task :build_iphone_lib do
|
|
20
|
+
build_ios_library('iphoneos')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
task :build_simulator_lib do
|
|
24
|
+
build_ios_library('iphonesimulator')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
task :combine_libraries do
|
|
28
|
+
`lipo -create -output "dist/libFrankAfcProxy.a" "#{build_dir}/Release-iphoneos/libFrankAfcProxy.a" "#{build_dir}/Release-iphonesimulator/libFrankAfcProxy.a"`
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
task :build_ios_lib => [:build_iphone_lib, :build_simulator_lib, :combine_libraries]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
desc "clean build artifacts"
|
|
35
|
+
task :clean do
|
|
36
|
+
rm_rf 'dist'
|
|
37
|
+
rm_rf "#{build_dir}"
|
|
38
|
+
rm_rf 'FrankAfcProxy.xcodeproj/project.xcworkspace'
|
|
39
|
+
rm_rf 'FrankAfcProxy.xcodeproj/xcuserdata'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
task :prep_dist do
|
|
43
|
+
mkdir_p 'dist'
|
|
44
|
+
mkdir_p "#{build_dir}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc "build library"
|
|
48
|
+
task :build_lib => [:clean, :prep_dist, :build_ios_lib]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
task :copy_to_skeleton do
|
|
52
|
+
sh "cp -r dist/* frankAfcProxy-skeleton/"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc "build and copy into the skeleton dir."
|
|
56
|
+
task :build_for_release => [:build_lib, :copy_to_skeleton]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
desc "build library and build gem."
|
|
60
|
+
task :all => [:build_for_release, :build]
|
data/bin/frankAfcProxy
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'webrick'
|
|
4
|
+
require 'io/afc'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'daemon_spawn'
|
|
7
|
+
require 'thor'
|
|
8
|
+
require 'pp'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module FrankAfcProxy
|
|
12
|
+
|
|
13
|
+
class Server < WEBrick::HTTPServlet::AbstractServlet
|
|
14
|
+
def pp_logger(obj)
|
|
15
|
+
print "\x1b[1m"
|
|
16
|
+
pp obj
|
|
17
|
+
print "\x1b[m"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(server, opt)
|
|
21
|
+
super(server, opt)
|
|
22
|
+
@opt = opt
|
|
23
|
+
@in_header = '/Documents/in.header'
|
|
24
|
+
@in_body = '/Documents/in.body'
|
|
25
|
+
@out_header = '/Documents/out.header'
|
|
26
|
+
@out_body = '/Documents/out.body'
|
|
27
|
+
@@afc.truncate(@out_body, 0)
|
|
28
|
+
@@afc.truncate(@out_header, 0)
|
|
29
|
+
@@runloop = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def parseopt(req)
|
|
33
|
+
opt = req.header
|
|
34
|
+
opt.delete("host")
|
|
35
|
+
opt.inject({}){|s, v| s[v[0]] = v[1][0]; s}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def request_to_frank(method, req, res)
|
|
39
|
+
uri = req.request_uri
|
|
40
|
+
uri.port=37265
|
|
41
|
+
uri.host="localhost"
|
|
42
|
+
|
|
43
|
+
opt = parseopt(req)
|
|
44
|
+
opt["URI"] = uri.to_s
|
|
45
|
+
opt["Method"] = method.to_s
|
|
46
|
+
pp_logger [method, opt["URI"]]
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
@@afc.truncate(@out_body, 0)
|
|
50
|
+
@@afc.truncate(@out_header, 0)
|
|
51
|
+
@@afc.open(@in_body, IO::WRONLY){|f| f.write(req.body)}
|
|
52
|
+
@@afc.open(@in_header, IO::WRONLY){|f| f.write(opt.to_json)}
|
|
53
|
+
pp_logger [:waiting_response, opt["URI"]]
|
|
54
|
+
|
|
55
|
+
retry_cont = 2
|
|
56
|
+
while @@runloop
|
|
57
|
+
s = @@afc.getattr(@out_header)[:st_size]
|
|
58
|
+
if s==0
|
|
59
|
+
sleep 0.01
|
|
60
|
+
next
|
|
61
|
+
end
|
|
62
|
+
res.body = body = @@afc.open(@out_body){|f| f.read(-1)}
|
|
63
|
+
pp_logger [method, opt["URI"], s, body.size]
|
|
64
|
+
if body.size==0 and retry_cont>0
|
|
65
|
+
retry_cont-=1
|
|
66
|
+
sleep 0.01
|
|
67
|
+
next
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
header = JSON.parse(@@afc.open(@out_header){|f| f.read(-1)})
|
|
72
|
+
res["Content-Type"] = header["Content-Type"]
|
|
73
|
+
res.status = header["Status"]
|
|
74
|
+
rescue JSON::ParserError => e
|
|
75
|
+
pp_logger e
|
|
76
|
+
res.status = 500
|
|
77
|
+
end
|
|
78
|
+
break
|
|
79
|
+
end
|
|
80
|
+
rescue => e
|
|
81
|
+
pp_logger [method, opt["URI"], e, e.backtrace[0..5]]
|
|
82
|
+
ensure
|
|
83
|
+
@@afc.truncate(@out_header, 0)
|
|
84
|
+
@@afc.truncate(@out_body, 0)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def do_GET(req, res)
|
|
89
|
+
request_to_frank(:GET, req, res)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def do_POST(req, res)
|
|
93
|
+
request_to_frank(:POST, req, res)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.start(opt)
|
|
97
|
+
@@afc = opt[:afc]
|
|
98
|
+
@@server = WEBrick::HTTPServer.new({:Port => opt['port'], :BindAddress => '0.0.0.0', :MaxClients=>1})
|
|
99
|
+
@@server.mount('/', self, opt)
|
|
100
|
+
|
|
101
|
+
trap('INT') {
|
|
102
|
+
@@server.stop
|
|
103
|
+
}
|
|
104
|
+
@@server.start
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def self.stop
|
|
108
|
+
@@runloop = false
|
|
109
|
+
@@server.shutdown
|
|
110
|
+
@@afc.close
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class Daemon < DaemonSpawn::Base
|
|
116
|
+
def self.exec(command, options)
|
|
117
|
+
@@opt = options.to_hash
|
|
118
|
+
@@opt[:afc] = IO::AFC.connect(options) if %w{start restart}.include?(command)
|
|
119
|
+
body = File.expand_path(File.join(options[:log], "#{File.basename(__FILE__, ".rb")}_#{options[:port]}"))
|
|
120
|
+
opt = {
|
|
121
|
+
:working_dir => '/',
|
|
122
|
+
:pid_file => body + '.pid',
|
|
123
|
+
:log_file => body + '.log',
|
|
124
|
+
:sync_log => true,
|
|
125
|
+
:singleton => false,
|
|
126
|
+
}
|
|
127
|
+
ARGV.clear
|
|
128
|
+
ARGV << command
|
|
129
|
+
if options[:debug]
|
|
130
|
+
Server.start(@@opt)
|
|
131
|
+
else
|
|
132
|
+
self.spawn!(opt)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def start(args)
|
|
137
|
+
Server.start(@@opt)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def stop
|
|
141
|
+
Server.stop
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class Cli < Thor
|
|
147
|
+
|
|
148
|
+
desc "start [option]", "Start frankAfcProxy."
|
|
149
|
+
option :appid, :type=>:string, :desc=>"Bundle identifer of your application.", :aliases=>'-a', :required=>true
|
|
150
|
+
option :udid, :type=>:string, :desc=>"UDID of the device you want to connect. Default is 'auto detect'.", :default=>nil
|
|
151
|
+
option :port, :type=>:numeric, :desc=>"Listen port.", :default=>4000, :aliases=>'-p'
|
|
152
|
+
option :log, :type=>:string, :desc=>"Log/pid files directory.", :default=>"/var/log", :aliases=>'-l'
|
|
153
|
+
option :debug, :type=>:boolean, :desc=>"Debug mode.", :aliases=>'-d'
|
|
154
|
+
def start
|
|
155
|
+
Daemon.exec('start', options)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
desc "stop [option]", "Stop frankAfcProxy."
|
|
159
|
+
option :port, :type=>:numeric, :desc=>"Listen port.", :default=>4000, :aliases=>'-p'
|
|
160
|
+
option :log, :type=>:string, :desc=>"Log/pid files directory.", :default=>"/var/log", :aliases=>'-l'
|
|
161
|
+
def stop
|
|
162
|
+
Daemon.exec('stop', options)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
desc "restart [option]", "Restart frankAfcProxy."
|
|
166
|
+
option :appid, :type=>:string, :desc=>"Bundle identifer of your application.", :aliases=>'-a', :required=>true
|
|
167
|
+
option :udid, :type=>:string, :desc=>"UDID of the device you want to connect. Default is 'auto detect'.", :default=>nil
|
|
168
|
+
option :port, :type=>:numeric, :desc=>"Listen port.", :default=>4000, :aliases=>'-p'
|
|
169
|
+
option :log, :type=>:string, :desc=>"Log/pid files directory.", :default=>"/var/log", :aliases=>'-l'
|
|
170
|
+
def restart
|
|
171
|
+
Daemon.exec('restart', options)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
desc "status [option]", "Show frankAfcProxy status."
|
|
175
|
+
option :port, :type=>:numeric, :desc=>"Listen port.", :default=>4000, :aliases=>'-p'
|
|
176
|
+
option :log, :type=>:string, :desc=>"Log/pid files directory.", :default=>"/var/log", :aliases=>'-l'
|
|
177
|
+
def status
|
|
178
|
+
Daemon.exec('status', options)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
desc "list [option]", "List installed applications."
|
|
182
|
+
option :udid, :type=>:string, :desc=>"UDID of the device you want to connect. Default is 'auto detect'.", :default=>nil
|
|
183
|
+
def list
|
|
184
|
+
begin
|
|
185
|
+
afc = IO::AFC.connect(:udid=>options[:udid])
|
|
186
|
+
rescue IO::AFC::NoDevice, IO::AFC::PasswordProtected, IO::AFC::ConnectFailed, StandardError => e
|
|
187
|
+
$stderr.puts e
|
|
188
|
+
return 1
|
|
189
|
+
end
|
|
190
|
+
puts "udid : #{afc.device_udid}"
|
|
191
|
+
apps = afc.applications
|
|
192
|
+
apps.each{|k, v|
|
|
193
|
+
puts "#{k} : #{v['CFBundleDisplayName']}"
|
|
194
|
+
}
|
|
195
|
+
puts "#{apps.size} application(s) is installed."
|
|
196
|
+
return 0
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
FrankAfcProxy::Cli.start(ARGV)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'frank-cucumber/version'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
FRANKIFY_XCCONFIG = 'Frank/frankify.xcconfig'
|
|
7
|
+
LIBPROXYFLAG = '-lFrankAfcProxy'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
puts "Patching Frank-Cucumber evniroments..."
|
|
11
|
+
begin
|
|
12
|
+
config = []
|
|
13
|
+
File.open(FRANKIFY_XCCONFIG).each_line{|line|
|
|
14
|
+
line.strip!
|
|
15
|
+
if line=~%r{^FRANK_LDFLAGS} and !(line=~/#{LIBPROXYFLAG}/)
|
|
16
|
+
line += " #{LIBPROXYFLAG}"
|
|
17
|
+
end
|
|
18
|
+
config << line
|
|
19
|
+
}
|
|
20
|
+
File.open(FRANKIFY_XCCONFIG, "w"){|f|
|
|
21
|
+
f.write config.join("\n")
|
|
22
|
+
}
|
|
23
|
+
rescue Errno::ENOENT => e
|
|
24
|
+
puts "... patch failed. #{e}"
|
|
25
|
+
puts "Please run the `frank setup` first."
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
puts 'Installing `libFrankAfcProxy`...'
|
|
31
|
+
FileUtils.cp( File.expand_path('../../frankAfcProxy-skeleton/libFrankAfcProxy.a', __FILE__), 'Frank/' )
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'frank_afc_proxy/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "frank_afc_proxy"
|
|
8
|
+
spec.version = FrankAfcProxy::VERSION
|
|
9
|
+
spec.authors = ["Toshiyuki Suzumura"]
|
|
10
|
+
spec.email = ["suz.labo@amail.plala.or.jp"]
|
|
11
|
+
spec.description = %q{Connect to FrankServer with iPhone USB cable via afc.}
|
|
12
|
+
spec.summary = %q{Connect to FrankServer with iPhone USB cable via afc.}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
spec.add_dependency "frank-cucumber", "~> 1.1.12"
|
|
24
|
+
spec.add_dependency "io-afc"
|
|
25
|
+
spec.add_dependency "daemon-spawn"
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: frank_afc_proxy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Toshiyuki Suzumura
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-06-28 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: bundler
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.3'
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.3'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: frank-cucumber
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ~>
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 1.1.12
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 1.1.12
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: io-afc
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
type: :runtime
|
|
71
|
+
prerelease: false
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
|
+
name: daemon-spawn
|
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
|
81
|
+
none: false
|
|
82
|
+
requirements:
|
|
83
|
+
- - ! '>='
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
type: :runtime
|
|
87
|
+
prerelease: false
|
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
90
|
+
requirements:
|
|
91
|
+
- - ! '>='
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
description: Connect to FrankServer with iPhone USB cable via afc.
|
|
95
|
+
email:
|
|
96
|
+
- suz.labo@amail.plala.or.jp
|
|
97
|
+
executables:
|
|
98
|
+
- frankAfcProxy
|
|
99
|
+
- frankAfcProxy-install
|
|
100
|
+
extensions: []
|
|
101
|
+
extra_rdoc_files: []
|
|
102
|
+
files:
|
|
103
|
+
- .gitignore
|
|
104
|
+
- FrankAfcProxy.xcodeproj/project.pbxproj
|
|
105
|
+
- FrankAfcProxy/FileChangeObserver.h
|
|
106
|
+
- FrankAfcProxy/FileChangeObserver.m
|
|
107
|
+
- FrankAfcProxy/FrankAfcProxy-Prefix.pch
|
|
108
|
+
- FrankAfcProxy/FrankAfcProxy.h
|
|
109
|
+
- FrankAfcProxy/FrankAfcProxy.m
|
|
110
|
+
- FrankAfcProxy/FrankAfcProxyLoader.h
|
|
111
|
+
- FrankAfcProxy/FrankAfcProxyLoader.m
|
|
112
|
+
- Gemfile
|
|
113
|
+
- LICENSE.txt
|
|
114
|
+
- README.md
|
|
115
|
+
- Rakefile
|
|
116
|
+
- bin/frankAfcProxy
|
|
117
|
+
- bin/frankAfcProxy-install
|
|
118
|
+
- frankAfcProxy-skeleton/libFrankAfcProxy.a
|
|
119
|
+
- frank_afc_proxy.gemspec
|
|
120
|
+
- lib/frank_afc_proxy.rb
|
|
121
|
+
- lib/frank_afc_proxy/version.rb
|
|
122
|
+
homepage: ''
|
|
123
|
+
licenses:
|
|
124
|
+
- MIT
|
|
125
|
+
post_install_message:
|
|
126
|
+
rdoc_options: []
|
|
127
|
+
require_paths:
|
|
128
|
+
- lib
|
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
|
+
none: false
|
|
131
|
+
requirements:
|
|
132
|
+
- - ! '>='
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '0'
|
|
135
|
+
segments:
|
|
136
|
+
- 0
|
|
137
|
+
hash: 1475162288700299665
|
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
|
+
none: false
|
|
140
|
+
requirements:
|
|
141
|
+
- - ! '>='
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
segments:
|
|
145
|
+
- 0
|
|
146
|
+
hash: 1475162288700299665
|
|
147
|
+
requirements: []
|
|
148
|
+
rubyforge_project:
|
|
149
|
+
rubygems_version: 1.8.25
|
|
150
|
+
signing_key:
|
|
151
|
+
specification_version: 3
|
|
152
|
+
summary: Connect to FrankServer with iPhone USB cable via afc.
|
|
153
|
+
test_files: []
|