├── LICENSE-MIT.txt
├── README.md
├── XObjC4.workflow
└── Contents
│ ├── Info.plist
│ ├── QuickLook
│ └── Preview.png
│ └── document.wflow
├── example
├── DemoViewController.h
└── DemoViewController.m
├── exampleXcode
├── Test1.h
├── Test1.m
├── Test2.h
├── Test2.m
└── exampleXcode.xcodeproj
│ └── project.pbxproj
├── website
├── demo-h.png
├── demo-m.png
└── xcode4scheme.png
├── xobjc.h
├── xobjc.py
├── xobjc4.py
└── xpublicmove.py
/LICENSE-MIT.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Dirk Holtwick
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | **Deprecated, this code should not be used any more! Better use Xcode 4 with ARC!**
4 |
5 | ---
6 |
7 |
8 | xobjc
9 | =====
10 |
11 | [](https://flattr.com/submit/auto?user_id=holtwick&url=https://github.com/holtwick/xobjc&title=xobjc&language=en_GB&tags=github&category=software)
12 |
13 | **FULL XCODE4 COMPATIBILITY***
14 |
15 | **Xcode4 Demo on YouTube [youtube.com/watch?v=Mzfv-ANydW4](http://www.youtube.com/watch?v=Mzfv-ANydW4).**
16 |
17 | **XCode3 Demo on mov.io [mov.io/fE](http://mov.io/fE).**
18 |
19 |
20 |
21 |
22 |
23 | A tool for making annoying every day tasks for Objective-C developers, especially
24 | iPhone developers, easier. Therefore this tool will help you writing semi-automatically
25 | the ``@property`` and ``@synthesize`` stuff. It also updates the ``dealloc`` method to release
26 | your objects. And you can define the attributes according to the
27 | [Google Objective-C Styleguide](http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml#Variable_Name)
28 | using a trailing underscore, i.e. your public attribute is then defined
29 | without this underscore. But also a leading underscore is ok. Public method can easily be defined by
30 | prepending XPUBLIC in the implementation part.
31 |
32 | No more redundancies and nonsense typing orgies! You'll save a lot of time!
33 |
34 | Preparations
35 | ------------
36 |
37 | You have to define some helpers in your code that will indicate your properties
38 | (you can alternatively use ``xobjc.h`` from this package):
39 |
40 | #define XRETAIN
41 | #define XIBOUTLET
42 | #define XASSIGN
43 | #define XCOPY
44 | #define XPROPERTY(...)
45 | #define XNIL nil
46 | #define XPUBLIC
47 |
48 | Your code
49 | ---------
50 |
51 | In your header file mark you properties like this:
52 |
53 | #import
54 | #import "xobjc.h"
55 |
56 | @interface DemoViewController : UIViewController {
57 | XIBOUTLET UILabel *header_;
58 | XIBOUTLET UITextView *message_;
59 | XASSIGN id delegate_;
60 | XASSIGN int counter;
61 | XRETAIN NSArray *listOfUsers_;
62 | XCOPY NSString *secretKey_;
63 | XPROPERTY(readonly) BOOL isPublic_;
64 | int age;
65 | }
66 |
67 | @end
68 |
69 | In your module file you can mark public methods like this:
70 |
71 | XPUBLIC
72 | - (void)somePubMethod { /* ... */ }
73 |
74 | (IBAction, class initializers and class methods are always considered public)
75 |
76 | Usage
77 | -----
78 |
79 | ``$ python xobjc.py sample.h``
80 |
81 | After that your ``.h`` and ``.m`` files are updated. XCode should reload your code in the editor
82 | automatically. The command also creates a new subfolder which contains a backup of your
83 | original code, hope you never will need it ;)
84 |
85 | Example
86 | -------
87 |
88 | Some pictures say more than thousand words. Here is a before/after diff showing the magic:
89 |
90 | 
91 |
92 | 
93 |
94 | XCode4 Integration
95 | ------------------
96 |
97 | Edit your projects 'Scheme' and add a new 'Run Script Action' to your 'Build / Pre-actions'.
98 | Fill in the full path of your xobjc4.py. Now each time you build your project this script
99 | will be run. This is super handy.
100 |
101 | 
102 |
103 | If Growl is installed it will also issue a notification.
104 |
105 |
106 | XCode3 Integration
107 | ------------------
108 |
109 | In Xcode in the menu choose the script symbol and there ``Edit User Scripts``. Create a new
110 | entry and copy and paste the contents of the file ``xobjc.py`` into the text field. That's all,
111 | you are done. Just call the script form the menu or via the key shortcut you defined while you
112 | are in the header or implementation file you like to become updated.
113 |
114 | Related
115 | -------
116 |
117 | Macro extensions for XCode
118 |
119 | * [xcode-text-macros](http://github.com/liyanage/xcode-text-macros)
120 |
121 | Similar approaches and discussions
122 |
123 | *
124 | *
125 | *
126 |
127 | Known Bugs and Problems
128 | -----------------------
129 |
130 | * With Snow Leopard MacOS X 10.6 the 'osascript' helper, which is used for XCode integration, threw some
131 | errors. I used a workaround I found here to circumvent the problem: http://kb2.adobe.com/cps/516/cpsid_51615.html
132 | If you are using non Intel architecture or other OS you might need to modify this portion of code in xobjc.py
133 |
134 | License (MIT License)
135 | ---------------------
136 |
137 | Copyright (c) 2011 Dirk Holtwick
138 |
139 | Permission is hereby granted, free of charge, to any person obtaining a copy
140 | of this software and associated documentation files (the "Software"), to deal
141 | in the Software without restriction, including without limitation the rights
142 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
143 | copies of the Software, and to permit persons to whom the Software is
144 | furnished to do so, subject to the following conditions:
145 |
146 | The above copyright notice and this permission notice shall be included in
147 | all copies or substantial portions of the Software.
148 |
149 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
150 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
151 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
152 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
153 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
154 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
155 | THE SOFTWARE.
156 |
157 |
--------------------------------------------------------------------------------
/XObjC4.workflow/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSServices
6 |
7 |
8 | NSMenuItem
9 |
10 | default
11 | XObjC4
12 |
13 | NSMessage
14 | runWorkflowAsService
15 | NSRequiredContext
16 |
17 | NSApplicationIdentifier
18 | com.apple.dt.Xcode
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/XObjC4.workflow/Contents/QuickLook/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/holtwick/xobjc/e83c2052783021c4e345b5cc826e1246b6c898b2/XObjC4.workflow/Contents/QuickLook/Preview.png
--------------------------------------------------------------------------------
/XObjC4.workflow/Contents/document.wflow:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AMApplicationBuild
6 | 247.1
7 | AMApplicationVersion
8 | 2.1.1
9 | AMDocumentVersion
10 | 2
11 | actions
12 |
13 |
14 | action
15 |
16 | AMAccepts
17 |
18 | Container
19 | List
20 | Optional
21 |
22 | Types
23 |
24 | com.apple.applescript.object
25 |
26 |
27 | AMActionVersion
28 | 1.0
29 | AMParameterProperties
30 |
31 | source
32 |
33 |
34 | AMProvides
35 |
36 | Container
37 | List
38 | Types
39 |
40 | com.apple.applescript.object
41 |
42 |
43 | ActionBundlePath
44 | /System/Library/Automator/Run AppleScript.action
45 | ActionName
46 | Run AppleScript
47 | ActionParameters
48 |
49 | source
50 | tell application id "com.apple.dt.Xcode"
51 | -- save all open source files
52 | -- save source documents
53 | -- return paths as a list
54 | return path of source documents
55 |
56 | end tell
57 |
58 | Application
59 |
60 | Automator
61 |
62 | BundleIdentifier
63 | com.apple.Automator.RunScript
64 | CFBundleVersion
65 | 1.0
66 | CanShowSelectedItemsWhenRun
67 |
68 | CanShowWhenRun
69 |
70 | Category
71 |
72 | AMCategoryUtilities
73 |
74 | Class Name
75 | RunScriptAction
76 | InputUUID
77 | AD908FE2-621A-4494-A987-6F448B85429E
78 | Keywords
79 |
80 | Run
81 |
82 | OutputUUID
83 | 38A88520-6EC4-4A10-A1BD-0F629967E4FC
84 | StartAction
85 |
86 | UUID
87 | 93416056-A9F3-4504-A086-3F8E71D051C6
88 | UnlocalizedApplications
89 |
90 | Automator
91 |
92 | arguments
93 |
94 | 0
95 |
96 | default value
97 | on run {input, parameters}
98 |
99 | (* Your script goes here *)
100 |
101 | return input
102 | end run
103 | name
104 | source
105 | required
106 | 0
107 | type
108 | 0
109 | uuid
110 | 0
111 |
112 |
113 | isViewVisible
114 |
115 | location
116 | 302.000000:597.000000
117 | nibPath
118 | /System/Library/Automator/Run AppleScript.action/Contents/Resources/English.lproj/main.nib
119 |
120 | isViewVisible
121 |
122 |
123 |
124 | action
125 |
126 | AMAccepts
127 |
128 | Container
129 | List
130 | Optional
131 |
132 | Types
133 |
134 | com.apple.cocoa.string
135 |
136 |
137 | AMActionVersion
138 | 2.0.1
139 | AMParameterProperties
140 |
141 | COMMAND_STRING
142 |
143 | CheckedForUserDefaultShell
144 |
145 | inputMethod
146 |
147 | shell
148 |
149 | source
150 |
151 |
152 | AMProvides
153 |
154 | Container
155 | List
156 | Types
157 |
158 | com.apple.cocoa.string
159 |
160 |
161 | ActionBundlePath
162 | /System/Library/Automator/Run Shell Script.action
163 | ActionName
164 | Run Shell Script
165 | ActionParameters
166 |
167 | COMMAND_STRING
168 | /Users/dirk/work/xobjc/xobjc4.py $@
169 | CheckedForUserDefaultShell
170 |
171 | inputMethod
172 | 1
173 | shell
174 | /bin/bash
175 | source
176 |
177 |
178 | Application
179 |
180 | Automator
181 |
182 | BundleIdentifier
183 | com.apple.RunShellScript
184 | CFBundleVersion
185 | 2.0.1
186 | CanShowSelectedItemsWhenRun
187 |
188 | CanShowWhenRun
189 |
190 | Category
191 |
192 | AMCategoryUtilities
193 |
194 | Class Name
195 | RunShellScriptAction
196 | InputUUID
197 | 3DF9515F-EA04-44F4-9370-5FC8E8F46CF4
198 | Keywords
199 |
200 | Shell
201 | Script
202 | Command
203 | Run
204 | Unix
205 |
206 | OutputUUID
207 | 82C3D6A5-64AF-4569-BD2C-B07CF4852F3D
208 | ShowWhenRun
209 |
210 | UUID
211 | 436E0C57-B266-4703-99D3-A95405A836CF
212 | UnlocalizedApplications
213 |
214 | Automator
215 |
216 | arguments
217 |
218 | 0
219 |
220 | default value
221 | 0
222 | name
223 | inputMethod
224 | required
225 | 0
226 | type
227 | 0
228 | uuid
229 | 0
230 |
231 | 1
232 |
233 | default value
234 |
235 | name
236 | source
237 | required
238 | 0
239 | type
240 | 0
241 | uuid
242 | 1
243 |
244 | 2
245 |
246 | default value
247 |
248 | name
249 | CheckedForUserDefaultShell
250 | required
251 | 0
252 | type
253 | 0
254 | uuid
255 | 2
256 |
257 | 3
258 |
259 | default value
260 |
261 | name
262 | COMMAND_STRING
263 | required
264 | 0
265 | type
266 | 0
267 | uuid
268 | 3
269 |
270 | 4
271 |
272 | default value
273 | /bin/sh
274 | name
275 | shell
276 | required
277 | 0
278 | type
279 | 0
280 | uuid
281 | 4
282 |
283 |
284 | isViewVisible
285 |
286 | location
287 | 302.000000:555.000000
288 | nibPath
289 | /System/Library/Automator/Run Shell Script.action/Contents/Resources/English.lproj/main.nib
290 |
291 | isViewVisible
292 |
293 |
294 |
295 | action
296 |
297 | AMAccepts
298 |
299 | Container
300 | List
301 | Optional
302 |
303 | Types
304 |
305 | com.apple.applescript.object
306 |
307 |
308 | AMActionVersion
309 | 1.0
310 | AMParameterProperties
311 |
312 | source
313 |
314 |
315 | AMProvides
316 |
317 | Container
318 | List
319 | Types
320 |
321 | com.apple.applescript.object
322 |
323 |
324 | ActionBundlePath
325 | /System/Library/Automator/Run AppleScript.action
326 | ActionName
327 | Run AppleScript
328 | ActionParameters
329 |
330 | source
331 | on run {input, parameters}
tell application "GrowlHelperApp"
-- Make a list of all the notification types
-- that this script will ever send:
set the allNotificationsList to {"XObjC"}
-- Make a list of the notifications
-- that will be enabled by default.
-- Those not enabled by default can be enabled later
-- in the 'Applications' tab of the growl prefpane.
set the enabledNotificationsList to {"XObjC"}
-- Register our script with growl.
-- You can optionally (as here) set a default icon
-- for this script's notifications.
register as application ¬
"XObjC" all notifications allNotificationsList ¬
default notifications enabledNotificationsList ¬
icon of application "XCode"
-- Send a Notification...
notify with name ¬
"XObjC" title ¬
"XCode" description ¬
(input as Unicode text) application name "XObjC"
end tell
end run
332 |
333 | Application
334 |
335 | Automator
336 |
337 | BundleIdentifier
338 | com.apple.Automator.RunScript
339 | CFBundleVersion
340 | 1.0
341 | CanShowSelectedItemsWhenRun
342 |
343 | CanShowWhenRun
344 |
345 | Category
346 |
347 | AMCategoryUtilities
348 |
349 | Class Name
350 | RunScriptAction
351 | InputUUID
352 | CB4C8E9B-5632-4F56-A996-E9002F338948
353 | Keywords
354 |
355 | Run
356 |
357 | OutputUUID
358 | 93F2AFD6-9D01-4E34-A51F-0CF28D2E025F
359 | UUID
360 | 15CCB17F-66BB-4691-A710-336F7FC09B7E
361 | UnlocalizedApplications
362 |
363 | Automator
364 |
365 | arguments
366 |
367 | 0
368 |
369 | default value
370 | on run {input, parameters}
371 |
372 | (* Your script goes here *)
373 |
374 | return input
375 | end run
376 | name
377 | source
378 | required
379 | 0
380 | type
381 | 0
382 | uuid
383 | 0
384 |
385 |
386 | isViewVisible
387 |
388 | location
389 | 302.000000:513.000000
390 | nibPath
391 | /System/Library/Automator/Run AppleScript.action/Contents/Resources/English.lproj/main.nib
392 |
393 | isViewVisible
394 |
395 |
396 |
397 | connectors
398 |
399 | 7736A740-3CC4-4C1E-BE4D-F8287026A58A
400 |
401 | from
402 | 93416056-A9F3-4504-A086-3F8E71D051C6 - 93416056-A9F3-4504-A086-3F8E71D051C6
403 | to
404 | 436E0C57-B266-4703-99D3-A95405A836CF - 436E0C57-B266-4703-99D3-A95405A836CF
405 |
406 | C47FDE74-7A30-4449-8A4F-538C42E3DE99
407 |
408 | from
409 | 436E0C57-B266-4703-99D3-A95405A836CF - 436E0C57-B266-4703-99D3-A95405A836CF
410 | to
411 | 15CCB17F-66BB-4691-A710-336F7FC09B7E - 15CCB17F-66BB-4691-A710-336F7FC09B7E
412 |
413 |
414 | state
415 |
416 | AMLogTabViewSelectedIndex
417 | 1
418 | libraryState
419 |
420 | actionsMajorSplitViewState
421 |
422 | expandedPosition
423 | 0.0
424 | subviewState
425 |
426 | 0.000000, 0.000000, 381.000000, 515.000000, NO
427 | 0.000000, 516.000000, 381.000000, 239.000000, NO
428 |
429 |
430 | actionsMinorSplitViewState
431 |
432 | expandedPosition
433 | 0.0
434 | subviewState
435 |
436 | 0.000000, 0.000000, 163.000000, 515.000000, NO
437 | 164.000000, 0.000000, 217.000000, 515.000000, NO
438 |
439 |
440 | variablesMajorSplitViewState
441 |
442 | expandedPosition
443 | 0.0
444 | subviewState
445 |
446 | 0.000000, 0.000000, 381.000000, 555.000000, NO
447 | 0.000000, 556.000000, 381.000000, 199.000000, NO
448 |
449 |
450 | variablesMinorSplitViewState
451 |
452 | expandedPosition
453 | 0.0
454 | subviewState
455 |
456 | 0.000000, 0.000000, 163.000000, 555.000000, NO
457 | 164.000000, 0.000000, 217.000000, 555.000000, NO
458 |
459 |
460 |
461 | majorSplitViewState
462 |
463 | expandedPosition
464 | 381
465 | subviewState
466 |
467 | 0.000000, 0.000000, 381.000000, 800.000000, NO
468 | 382.000000, 0.000000, 619.000000, 800.000000, NO
469 |
470 |
471 | minorSplitViewState
472 |
473 | expandedPosition
474 | 0.0
475 | subviewState
476 |
477 | 0.000000, 0.000000, 619.000000, 609.000000, NO
478 | 0.000000, 619.000000, 619.000000, 162.000000, NO
479 |
480 |
481 | windowFrame
482 | {{213, 86}, {1000, 877}}
483 | workflowViewScrollPosition
484 | {{0, 0}, {604, 609}}
485 |
486 | workflowMetaData
487 |
488 | serviceApplicationBundleID
489 | com.apple.dt.Xcode
490 | serviceApplicationPath
491 | /Xcode4/Applications/Xcode.app
492 | serviceInputTypeIdentifier
493 | com.apple.Automator.nothing
494 | serviceOutputTypeIdentifier
495 | com.apple.Automator.nothing
496 | workflowTypeIdentifier
497 | com.apple.Automator.servicesMenu
498 |
499 |
500 |
501 |
--------------------------------------------------------------------------------
/example/DemoViewController.h:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Apply `xobjc` to this file to see the effect
4 |
5 | */
6 |
7 | #import
8 | #import "xobjc.h"
9 |
10 | @interface DemoViewController : UIViewController {
11 | XIBOUTLET UILabel *header_;
12 | XIBOUTLET UITextView *message_;
13 | XASSIGN id _delegate;
14 | XASSIGN int counter;
15 | XRETAIN NSArray *listOfUsers_;
16 | XCOPY NSString *secretKey_;
17 | int age;
18 | }
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/example/DemoViewController.m:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Apply `xobjc` to this file to see the effect
4 |
5 | */
6 |
7 | #import "DemoViewController.h"
8 |
9 | @implementation DemoViewController
10 |
11 | // X PUBLIC
12 | - (IBAction)doSomething:(id)sender {
13 | NSLog(@"Doing something");
14 | }
15 |
16 | - (void)viewDidUnload{
17 | [super viewDidUnload];
18 | self.delegate = nil;
19 | }
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/exampleXcode/Test1.h:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2010 holtwick.it. All rights reserved.
3 | //
4 |
5 | @interface Test1 : NSObject {
6 | id x;
7 | }
8 |
9 | @property (retain) id x;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/exampleXcode/Test1.m:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2010 holtwick.it. All rights reserved.
3 | //
4 |
5 | #import "Test1.h"
6 |
7 | @implementation Test1
8 |
9 | @synthesize x;
10 |
11 | // MARK: Methods
12 |
13 | // ...
14 |
15 | // MARK: Constructors
16 |
17 | /*
18 | + (id)instance {
19 | static id instance = nil;
20 | @synchronized (self) {
21 | if (!instance) {
22 | instance = [[self alloc] init];
23 | }
24 | }
25 | return instance;
26 | }
27 |
28 | - (id)initWithString:(NSString *)string {
29 | self = [self init];
30 | if (self != nil) {
31 | self.string = string;
32 | }
33 | return self;
34 | }
35 |
36 | - (id)init {
37 | self = [super init];
38 | if (self != nil) {
39 | ;
40 | }
41 | return self;
42 | }
43 | */
44 |
45 | - (void)dealloc{
46 | [x release]
47 | [super dealloc];
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/exampleXcode/Test2.h:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2010 holtwick.it. All rights reserved.
3 | //
4 |
5 | #import "xobjc.h"
6 |
7 | @interface Test2 : NSObject {
8 | XRETAIN id x;
9 | }
10 |
11 | @property (retain) id x;
12 |
13 | @end
--------------------------------------------------------------------------------
/exampleXcode/Test2.m:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2010 holtwick.it. All rights reserved.
3 | //
4 |
5 | #import "Test2.h"
6 |
7 | @implementation Test2
8 |
9 | // MARK: Methods
10 |
11 | // ...
12 |
13 | // MARK: Constructors
14 |
15 | /*
16 | + (id)instance {
17 | static id instance = nil;
18 | @synchronized (self) {
19 | if (!instance) {
20 | instance = [[self alloc] init];
21 | }
22 | }
23 | return instance;
24 | }
25 |
26 | - (id)initWithString:(NSString *)string {
27 | self = [self init];
28 | if (self != nil) {
29 | self.string = string;
30 | }
31 | return self;
32 | }
33 |
34 | - (id)init {
35 | self = [super init];
36 | if (self != nil) {
37 | ;
38 | }
39 | return self;
40 | }
41 | */
42 |
43 | - (void)dealloc {
44 | [x xrelease];
45 | [super dealloc];
46 | }
47 |
48 | @synthesize x;
49 |
50 | @end
--------------------------------------------------------------------------------
/exampleXcode/exampleXcode.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 42;
7 | objects = {
8 |
9 | /* Begin PBXFileReference section */
10 | 438D64E411FF684700F87B1A /* Test1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test1.h; sourceTree = ""; };
11 | 438D64E511FF684700F87B1A /* Test1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test1.m; sourceTree = ""; };
12 | 438D64F511FF68A300F87B1A /* Test2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test2.h; sourceTree = ""; };
13 | 438D64F611FF68A300F87B1A /* Test2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test2.m; sourceTree = ""; };
14 | 438D64F711FF68C400F87B1A /* xobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xobjc.h; path = ../xobjc.h; sourceTree = SOURCE_ROOT; };
15 | /* End PBXFileReference section */
16 |
17 | /* Begin PBXGroup section */
18 | 438D64DA11FF683600F87B1A = {
19 | isa = PBXGroup;
20 | children = (
21 | 438D64F711FF68C400F87B1A /* xobjc.h */,
22 | 438D64F511FF68A300F87B1A /* Test2.h */,
23 | 438D64F611FF68A300F87B1A /* Test2.m */,
24 | 438D64E411FF684700F87B1A /* Test1.h */,
25 | 438D64E511FF684700F87B1A /* Test1.m */,
26 | );
27 | sourceTree = "";
28 | };
29 | /* End PBXGroup section */
30 |
31 | /* Begin PBXProject section */
32 | 438D64DC11FF683600F87B1A /* Project object */ = {
33 | isa = PBXProject;
34 | buildConfigurationList = 438D64DF11FF683600F87B1A /* Build configuration list for PBXProject "exampleXcode" */;
35 | compatibilityVersion = "Xcode 2.4";
36 | hasScannedForEncodings = 0;
37 | mainGroup = 438D64DA11FF683600F87B1A;
38 | projectDirPath = "";
39 | projectRoot = "";
40 | targets = (
41 | );
42 | };
43 | /* End PBXProject section */
44 |
45 | /* Begin XCBuildConfiguration section */
46 | 438D64DD11FF683600F87B1A /* Debug */ = {
47 | isa = XCBuildConfiguration;
48 | buildSettings = {
49 | COPY_PHASE_STRIP = NO;
50 | };
51 | name = Debug;
52 | };
53 | 438D64DE11FF683600F87B1A /* Release */ = {
54 | isa = XCBuildConfiguration;
55 | buildSettings = {
56 | COPY_PHASE_STRIP = YES;
57 | };
58 | name = Release;
59 | };
60 | /* End XCBuildConfiguration section */
61 |
62 | /* Begin XCConfigurationList section */
63 | 438D64DF11FF683600F87B1A /* Build configuration list for PBXProject "exampleXcode" */ = {
64 | isa = XCConfigurationList;
65 | buildConfigurations = (
66 | 438D64DD11FF683600F87B1A /* Debug */,
67 | 438D64DE11FF683600F87B1A /* Release */,
68 | );
69 | defaultConfigurationIsVisible = 0;
70 | defaultConfigurationName = Release;
71 | };
72 | /* End XCConfigurationList section */
73 | };
74 | rootObject = 438D64DC11FF683600F87B1A /* Project object */;
75 | }
76 |
--------------------------------------------------------------------------------
/website/demo-h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/holtwick/xobjc/e83c2052783021c4e345b5cc826e1246b6c898b2/website/demo-h.png
--------------------------------------------------------------------------------
/website/demo-m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/holtwick/xobjc/e83c2052783021c4e345b5cc826e1246b6c898b2/website/demo-m.png
--------------------------------------------------------------------------------
/website/xcode4scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/holtwick/xobjc/e83c2052783021c4e345b5cc826e1246b6c898b2/website/xcode4scheme.png
--------------------------------------------------------------------------------
/xobjc.h:
--------------------------------------------------------------------------------
1 | //
2 | // xobjc.h
3 | //
4 | // Created by Dirk Holtwick
5 | // Copyright holtwick.it 2009. All rights reserved.
6 | //
7 |
8 | #define XRETAIN
9 | #define XIBOUTLET
10 | #define XASSIGN
11 | #define XCOPY
12 | #define XPROPERTY(...)
13 | #define XDELEGATE
14 |
15 | #define XPUBLIC
16 |
17 | #define xobjc
18 | #define XNIL nil
19 | #define xnil nil
20 | #define xrelease release
21 |
22 |
--------------------------------------------------------------------------------
/xobjc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | """
5 | Copyright (c) 2009 Dirk Holtwick
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 | CHANGELOG:
26 |
27 | 0.1
28 | - Initial release
29 |
30 | 0.2 (2009-09-04)
31 | - Dealloc can contain custom data
32 | - Adds missing dealloc correctly
33 |
34 | 0.3 (2009-09-11)
35 | - viewDidUnload support
36 |
37 | 0.4 (2009-12-18)
38 | - Joined with version of 'freewizard' with leading underscore support
39 | - Some refactoring to make the source more readable
40 | - Added XPUBLIC to mark public methods
41 | - Alphabetical sorting of blocks
42 | - Added MIT license
43 |
44 | 0.5 (2010-01-05)
45 | - More refactoring
46 | - Now work also when no properties are defined
47 | - Prefix XPUBLIC also in Interface file to prepare for more intelligent
48 | handling of those marked methods in future
49 | - Removed unsexy whitesapce
50 |
51 | 0.6 (2010-01-06)
52 | - Removed the prepending XPUBLIC from interface file because Interface
53 | Builder was not able to handle it
54 | - IBAction methods are always considered public (I don't see a case where
55 | they are not
56 | - Static methods are also considered public
57 |
58 | 0.7 (2010-02-07)
59 | - Fix for Apple Script calls
60 | - Handle comment correctly
61 | - New FORCE_METHODS setting
62 |
63 | 0.8 (2010-02-15)
64 | - Moved @synthesize to the end of files
65 | - Moved @property to the end of files
66 |
67 | 0.9 (2010-03-14)
68 | - Methods which start with 'initWith' are considered public
69 | - Added XPROPERTY(..) for individual property definitions, e.g.
70 | XPROPERTY(readonly) id test;
71 | - Removed XATOMIC and XREADONLY
72 | - Code cleanup
73 |
74 | 0.10
75 | - Now also works as build script
76 | - Only handle files if different file times
77 | - Only change files that contain X... macros
78 | - Nonatomic can be turned of as default
79 | - Strip trailing spaces
80 | - Multiple file and path arguments
81 | - Added XDELEGATE
82 |
83 | 0.11 (2010-09-07)
84 | - Fix: Can handle missing spaces around asterisk
85 | - External settings did not work, therefore removed
86 |
87 | 0.12 (2010-09-21)
88 | - Categories support
89 | - Expand environment variables for backup path
90 | - Support for .mm file suffixes
91 | - DEBUG does not write files
92 |
93 | 0.13 (2011-01-08)
94 | - XPROPERTY arguments case sensitive
95 | - Added 'xobjc' marker into @property
96 | - 'XASSIGN BOOL xxx' creates corect 'isXxx' getter
97 |
98 | TODO:
99 |
100 | - Work with more implementations etc. in one file and match name
101 | => Currently just one implementation per file
102 | - NSCoder support
103 | => Create all needed stuff
104 | - XPRIVATE
105 | => Put them into a category in the implementation file
106 |
107 | """
108 |
109 | __version__ = "0.13"
110 |
111 | import re
112 | import os
113 | import os.path
114 | import shutil
115 | import pprint
116 | import datetime
117 | import subprocess
118 |
119 | ### CONFIG BEGIN
120 |
121 | # !!! PLEASE CHANGE THE FOLLOWING TO YOU NEEDS !!!
122 |
123 | # No backup at all
124 | # BACKUP_FOLDER = None
125 |
126 | # Subfolder of the sources location
127 | # BACKUP_FOLDER = 'BACKUP-XOBJC'
128 |
129 | # All into one absolute path
130 | BACKUP_FOLDER = os.path.expandvars('${HOME}/work/_build/__xobjc_backup')
131 | DEBUG = 0
132 | FORCE_METHODS = False #True
133 | BOOL_WITH_IS_GETTER = True
134 | STRIP_TRAILING_SPACES = True
135 | NONATOMIC = ""
136 | # NONATOMIC = "nonatomic, "
137 |
138 | ### CONFIG END
139 |
140 | rxInterface = re.compile("""
141 | .*?
142 | @interface (?P .*?)
143 | \{
144 | (?P .*? )
145 | \}
146 | (?P .*?)
147 | @end
148 | .*?
149 | """, re.VERBOSE | re.M | re.DOTALL)
150 |
151 | rxInterfaceCat = re.compile("""
152 | .*?
153 | @interface
154 | (?P .*?)
155 | \(
156 | (?P .*?)
157 | \)
158 |
159 | (?P .*?)
160 | @end
161 | .*?
162 | """, re.VERBOSE | re.M | re.DOTALL)
163 | rxImplementation = re.compile("""
164 | .*?
165 | \@implementation\s+(?P[a-zA-Z0-9_]+)
166 | (?P .*?)
167 | \@end
168 | """, re.VERBOSE | re.M | re.DOTALL)
169 |
170 | rxDealloc = re.compile("""
171 | \-\s*\(void\)\s*
172 | dealloc
173 | \s*
174 | \{
175 | (?P .*?)
176 | (\[\s*[^\s]+\s+x?release\s*\]\s*\;\s*)*
177 | \[\s*super\s+dealloc\s*\]\s*\;\s*
178 | \}
179 | """, re.VERBOSE | re.M | re.DOTALL)
180 |
181 | rxViewDidUnload = re.compile("""
182 | \-\s*\(void\)\s*
183 | viewDidUnload
184 | \s*
185 | \{
186 | (?P [^\}]*?)
187 | \}
188 | """, re.VERBOSE | re.M | re.DOTALL)
189 |
190 | rxViewDidUnloadBody = re.compile("""
191 | \[\s*super\s+viewDidUnload\s*\]\s*\;
192 | |
193 | self\.[a-zA-Z0-9_]+ \s* \= \s* (xnil|XNIL) \s* \;
194 | """, re.VERBOSE | re.M | re.DOTALL)
195 |
196 | rxVariables = re.compile("""
197 | (XCOPY | XASSIGN | XRETAIN | XIBOUTLET | XDELEGATE | IBOutlet | XPROPERTY\(.*?\))
198 | \s+
199 | ([a-zA-Z0-9_][a-zA-Z0-9_\<\>]*)
200 | ((
201 | (?:
202 | \s*\*
203 | |
204 | \s
205 | )
206 | \s*
207 | [a-zA-Z0-9_]+
208 | \s*
209 | \,?
210 | \s*
211 | )+)
212 | \;
213 | """, re.VERBOSE | re.M | re.DOTALL)
214 |
215 | rxProperty = re.compile("""
216 | \@property
217 |
218 | \s*
219 |
220 | (
221 | \(
222 | .*?
223 | \)
224 | )?
225 |
226 | \s*
227 |
228 | ([a-zA-Z0-9_][a-zA-Z0-9_\<\>]*)
229 |
230 | \s+
231 |
232 | ((
233 | \*?
234 | \s*
235 | [a-zA-Z0-9_]+
236 | \s*
237 | \,?
238 | \s*
239 | )+)
240 | \;
241 |
242 | """, re.VERBOSE | re.M | re.DOTALL)
243 |
244 | rxSynthesize = re.compile("""
245 | \@synthesize
246 | \s+
247 | \w+
248 | \s*
249 | \=?
250 | \s*
251 | \w*
252 | \;
253 | """, re.VERBOSE | re.M | re.DOTALL)
254 |
255 | rxLeadingUnderscore = re.compile("(\s*\*?\s*)_(.+)")
256 |
257 | rxMethod = re.compile("""
258 | (?P
259 | (XPUBLIC)?
260 | )
261 | \s*
262 | (?P
263 | [\-\+]
264 | \s*
265 | \([^\)]+\)
266 | \s*
267 | [a-zA-Z_]
268 | [^\{\=]+?
269 | )
270 | \{
271 |
272 | """, re.VERBOSE | re.M | re.DOTALL)
273 |
274 | #rxInstance = re.compile("""
275 | # XINSTANCE([^\s]+)
276 | #""", re.VERBOSE | re.M | re.DOTALL)
277 |
278 | rxComment = re.compile("""
279 | (
280 | \/\*.*?\*\/
281 | |
282 | ^\s*\/\/.*?$
283 | )
284 | """, re.VERBOSE | re.M | re.DOTALL)
285 |
286 | rxInitMethod = re.compile("""
287 | \-\s*(.*?)\s*initWith.*
288 | """, re.VERBOSE | re.M | re.DOTALL)
289 |
290 | def mySorted(v, **k):
291 | return v
292 | # return sorted(v, **k)
293 |
294 | class Module:
295 |
296 | def __init__(filename):
297 | self.base = filename[:filename.rfind(".")]
298 | self.h = self.base + '.h'
299 | self.m = self.baee = ".m"
300 |
301 | def stripComments(value):
302 | #if DEBUG:
303 | # for c in rxComment.findall(value):
304 | # print c
305 | return rxComment.sub('', value)
306 |
307 | def extractVariables(data):
308 | return [x.strip() for x in data.strip().split(",")]
309 |
310 | def insertString(base, pos, new):
311 | return base[:pos] + new + base[pos:]
312 |
313 | def analyze(hdata, mdata):
314 |
315 | ### HEADER
316 |
317 | vars = dict()
318 |
319 | propBlock = []
320 | viewdidunload = []
321 | dealloc = []
322 | block = []
323 | isCategory = 0
324 |
325 | interfaceMatch = rxInterface.match(hdata)
326 | if not interfaceMatch:
327 | interfaceMatch = rxInterfaceCat.match(hdata)
328 | if not interfaceMatch:
329 | return None, None
330 | else:
331 | isCategory = 1
332 |
333 | if not isCategory:
334 | varblock = interfaceMatch.group("varblock")
335 | varblock = stripComments(varblock.strip())
336 |
337 | # Collect variable definitions
338 | for mv in rxVariables.finditer(varblock):
339 | mode, type_, names, names_ = mv.groups()
340 | for vname in extractVariables(names):
341 | vars[''.join(vname.split())] = (mode.lower(), type_, mode)
342 |
343 | # Remove @properties completely from interface
344 | properties = interfaceMatch.group("properties")
345 |
346 | if isCategory:
347 | for mpp in rxProperty.finditer(properties):
348 | propBlock.append(mpp.group(0))
349 |
350 | if not isCategory:
351 | properties = rxProperty.sub('', properties).lstrip()
352 |
353 | # Create @properties
354 | for vname in mySorted(vars.keys(), key=lambda k:k.strip('*').strip('_')):
355 | mode, type_, origMode = vars[vname]
356 |
357 | iboutlet = 0
358 | star = '*' if vname.startswith('*') else ''
359 | name = vname.lstrip('*') # Withoout leading *
360 | pvname = name # Without underscore
361 |
362 | # Google compatible synthesize
363 | if name.endswith('_'):
364 | pvname = name[:-1]
365 | block.append("@synthesize %s = %s;" % (pvname, name))
366 | elif name.startswith('_'):
367 | pvname = name[1:]
368 | block.append("@synthesize %s = %s;" % (pvname, name))
369 | else:
370 | block.append("@synthesize %s;" % (name))
371 |
372 | # Properties
373 | propMarker = "xobjc "
374 | if mode == 'iboutlet':
375 | iboutlet = 1
376 | mode = 'retain'
377 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
378 | elif mode == 'xiboutlet':
379 | iboutlet = 1
380 | mode = "retain"
381 | type_ = "IBOutlet %s" % type_
382 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
383 | elif mode == 'xdelegate':
384 | iboutlet = 1
385 | mode = "assign"
386 | type_ = "IBOutlet %s" % type_
387 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
388 | elif mode.startswith('xproperty('):
389 | # XXX Iboutlet
390 | pattr = origMode.strip()[10:-1]
391 | propBlock.append("@property (%s%s) %s %s%s;" % (propMarker, pattr, type_, star, pvname))
392 | mode = 'assign'
393 | pattrlist = [x.strip().lower() for x in pattr.split(',')]
394 | if 'retain' in pattrlist or 'copy' in pattrlist:
395 | mode = 'retain'
396 | else:
397 | mode = mode[1:]
398 | if BOOL_WITH_IS_GETTER and type_ == "BOOL":
399 | mode = "getter=is%s%s" % (pvname[0].capitalize(), pvname[1:])
400 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
401 |
402 | # print mode
403 |
404 | # Release stuff
405 | if mode in ('retain', 'copy'):
406 | dealloc.append(" [%s xrelease];" % name)
407 |
408 | if iboutlet:
409 | viewdidunload.append(" self.%s = xnil;" % pvname)
410 |
411 | # print viewdidunload
412 |
413 | propBlock = "\n".join(propBlock)
414 |
415 | ### MODULE
416 |
417 | # Find implementation blinterfaceMatch
418 | implementationMatch = rxImplementation.match(mdata)
419 | impName = implementationMatch.group('name')
420 |
421 | #if DEBUG and implementationMatch:
422 | # print "Implementation", implementationMatch.groups()
423 |
424 | # Replace @synthesize block
425 | body = implementationMatch.group("body")
426 |
427 | if not isCategory:
428 | body = rxSynthesize.sub('', body).strip()
429 | block = "\n".join(block) + '\n\n'
430 |
431 | # Update 'dealloc'
432 | md = rxDealloc.search(body)
433 | if md:
434 | # deallocbody = rxRelease.sub('', md.group("deallocbody")).strip()
435 | deallocbody = md.group("deallocbody").strip()
436 | if deallocbody:
437 | deallocbody = " " + deallocbody + "\n\n"
438 | newdealloc = ("- (void)dealloc { "
439 | + ("\n" + deallocbody).rstrip()
440 | + ("\n" + "\n".join(mySorted(dealloc))).rstrip()
441 | + "\n [super dealloc];\n}")
442 | body = rxDealloc.sub(newdealloc, body)
443 | else:
444 | newdealloc = "- (void)dealloc {\n" + "\n".join(mySorted(dealloc)) + "\n [super dealloc];\n}"
445 | body += "\n\n" + newdealloc
446 |
447 | # Update 'viewDidUnload' (iPhone and iPad only)
448 | md = rxViewDidUnload.search(body)
449 | if md:
450 | viewdidunloadbody = rxViewDidUnloadBody.sub('', md.group("viewdidunloadbody")).strip()
451 | if viewdidunloadbody:
452 | viewdidunloadbody = "\n " + viewdidunloadbody + "\n\n"
453 | newviewdidunloadbody = (
454 | "- (void)viewDidUnload {\n [super viewDidUnload];\n"
455 | + (" " + viewdidunloadbody.strip()).rstrip()
456 | + ("\n" + "\n".join(mySorted(viewdidunload))).rstrip()
457 | + "\n}")
458 | body = rxViewDidUnload.sub(newviewdidunloadbody, body)
459 |
460 | ### METHODS
461 | mDefs = []
462 | xpub = 0
463 | bodyStripped = stripComments(body)
464 |
465 | for mMethod in rxMethod.finditer(bodyStripped):
466 | mName = mMethod.group('name').strip()
467 | # if mMethod.group("comment"):
468 | # mName = "\n" + mMethod.group("comment").strip() + "\n" + mName
469 | #if DEBUG:
470 | # print mName, mMethod.groups()
471 | if (mMethod.group('kind') == 'XPUBLIC'):
472 | xpub += 1
473 | mDefs.append(mName + ';')
474 | elif mName.startswith("+") or mName.lstrip('-').lstrip().startswith("(IBAction)"):
475 | mDefs.append(mName + ';')
476 | elif rxInitMethod.match(mName):
477 | mDefs.append(mName + ';')
478 |
479 | ### XINSTANCE
480 | #mdi = rxInstance.search(bodyStripped)
481 | #if mdi:
482 | # xpub += 1
483 | # mDefs.append("+ (id)instance;")
484 |
485 | # If no XPUBLIC was defined don't replace old stuff
486 | if mDefs or FORCE_METHODS:
487 | # mDefs = "\n".join(mySorted(mDefs)) + '\n\n'
488 | mDefs = "\n".join(mDefs) + '\n\n'
489 | else:
490 | mDefs = properties
491 |
492 | ### RESULT
493 |
494 | if isCategory:
495 |
496 | hdata = (hdata[:interfaceMatch.start("properties")]
497 | + ('\n\n' + propBlock).rstrip()
498 | + ('\n\n' + mDefs).rstrip()
499 | + '\n\n' + hdata[interfaceMatch.end("properties"):])
500 |
501 | else:
502 |
503 | hdata = (hdata[:interfaceMatch.start("properties")]
504 | + ('\n\n' + mDefs).rstrip()
505 | + ('\n\n' + propBlock).rstrip()
506 | + '\n\n' + hdata[interfaceMatch.end("properties"):])
507 |
508 | mdata = (mdata[:implementationMatch.start('body')]
509 | + ('\n\n' + body).rstrip()
510 | + ('\n\n' + block).rstrip()
511 | + '\n\n' + mdata[implementationMatch.end('body'):])
512 |
513 | # Did something change?
514 | if xpub or propBlock:
515 | return hdata, mdata
516 |
517 | return None, None
518 |
519 | def modifyFiles(filename):
520 |
521 | # Calculate basic filenames
522 | base = os.path.normpath(os.path.abspath(filename))
523 | folder = os.path.dirname(base)
524 | filePart = os.path.basename(base)
525 |
526 | if filePart == "main.m":
527 | # print "File %r will not be modified" % filePart
528 | return False
529 |
530 | hfile = filename[:filename.rfind(".")] + '.h'
531 | mfile = filename[:filename.rfind(".")] + '.m'
532 |
533 | # Check if files exist
534 | if not os.path.isfile(hfile):
535 | # print "File %r does not exist" % hfile
536 | return False
537 | if not os.path.isfile(mfile):
538 | # print "File %r does not exist" % hfile
539 | mfile = filename[:filename.rfind(".")] + '.mm'
540 | if not os.path.isfile(mfile):
541 | return False
542 |
543 | htime = os.stat(hfile).st_mtime
544 | mtime = os.stat(mfile).st_mtime
545 |
546 | if htime == mtime:
547 | # print "No update needed"
548 | return False
549 |
550 | # Handle and modify files
551 | hsrc = open(hfile).read()
552 | msrc = open(mfile).read()
553 | if ("noxobjc" in hsrc.lower()) or ("noxobjc" in msrc.lower()):
554 | # print "File ignored"
555 | return False
556 |
557 | # Handle and modify files
558 | hdata, mdata = analyze(
559 | hsrc,
560 | msrc)
561 |
562 | if not (hdata and mdata):
563 | return False
564 |
565 | # Backup files
566 | if BACKUP_FOLDER:
567 | backupFolder = os.path.join(
568 | folder,
569 | BACKUP_FOLDER,
570 | 'backup-' + datetime.datetime.today().strftime("%Y%m%d-%H%M%S"))
571 | if not os.path.isdir(backupFolder):
572 | os.makedirs(backupFolder)
573 | shutil.copyfile(hfile, os.path.join(backupFolder, filePart[:-2] + '.h'))
574 | shutil.copyfile(mfile, os.path.join(backupFolder, filePart[:-2] + '.m'))
575 | # print "Created backup of files in %r" % backupFolder
576 |
577 |
578 | if STRIP_TRAILING_SPACES:
579 | hdata = "\n".join([l.rstrip() for l in hdata.splitlines()])
580 | mdata = "\n".join([l.rstrip() for l in mdata.splitlines()])
581 |
582 | if DEBUG:
583 | print "=" * 80
584 | print hfile
585 | print "=" * 80
586 | print hdata
587 | print "=" * 80
588 | print mfile
589 | print "=" * 80
590 | print mdata
591 |
592 | if not DEBUG:
593 | f = open(hfile, 'w')
594 | f.write(hdata)
595 | f.close()
596 |
597 | f = open(mfile, 'w')
598 | f.write(mdata)
599 | f.close()
600 |
601 | # Same file time
602 | subprocess.call(['touch', hfile, mfile])
603 |
604 | #print "Modified %r" % hfile
605 | #print "Modified %r" % mfile
606 |
607 | return True
608 |
609 | def xcodeReload():
610 | # Trick to reload files in XCode
611 | # Bug workaround for SL, see http://kb2.adobe.com/cps/516/cpsid_51615.html
612 | print "XCode refresh"
613 | subprocess.call(['arch', '-i386', 'osascript', '-e', 'activate application "Finder"\nactivate application "XCode"'])
614 |
615 | if __name__ == "__main__":
616 | import sys
617 | import glob
618 |
619 | # You can also place it into 'XCode User Scripts' but it does not relead the window yet
620 | try:
621 | filename = '%%%{PBXFilePath}%%%'
622 | except:
623 | filename = ''
624 |
625 | if filename and (not filename.startswith('%')):
626 | modifyFiles(filename)
627 | xcodeReload()
628 |
629 | elif len(sys.argv) >= 2:
630 | # srcroot = os.environ.get("SRCROOT")
631 |
632 | modified = False
633 | for filename in sys.argv[1:]:
634 |
635 | filename = os.path.abspath(filename)
636 | # print "Analyze %s" % filename
637 |
638 | mfiles = [filename]
639 | if os.path.isdir(filename):
640 | for root, dirs, files in os.walk(filename):
641 | for name in files:
642 | if (BACKUP_FOLDER not in root) and name.endswith(".m"):
643 | mfiles.append(os.path.join(root, name))
644 |
645 | # print "\n".join(mfiles)
646 |
647 | # elif srcroot:
648 | # files = glob.glob("Classes/*.m")
649 |
650 | if mfiles:
651 |
652 | for fn in mfiles:
653 | if modifyFiles(fn):
654 | print "Modified %r" % fn
655 | modified = True
656 |
657 | if modified:
658 | xcodeReload()
659 | else:
660 | print "No modifications needed"
661 |
662 | else:
663 | print "Usage: xobjc.py [file/folder paths]"
664 |
--------------------------------------------------------------------------------
/xobjc4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: UTF-8 -*-
3 |
4 | """
5 | Copyright (c) 2009-2012 Dirk Holtwick
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
24 |
25 | CHANGELOG:
26 |
27 | 0.1
28 | - Initial release
29 |
30 | 0.2 (2009-09-04)
31 | - Dealloc can contain custom data
32 | - Adds missing dealloc correctly
33 |
34 | 0.3 (2009-09-11)
35 | - viewDidUnload support
36 |
37 | 0.4 (2009-12-18)
38 | - Joined with version of 'freewizard' with leading underscore support
39 | - Some refactoring to make the source more readable
40 | - Added XPUBLIC to mark public methods
41 | - Alphabetical sorting of blocks
42 | - Added MIT license
43 |
44 | 0.5 (2010-01-05)
45 | - More refactoring
46 | - Now work also when no properties are defined
47 | - Prefix XPUBLIC also in Interface file to prepare for more intelligent
48 | handling of those marked methods in future
49 | - Removed unsexy whitesapce
50 |
51 | 0.6 (2010-01-06)
52 | - Removed the prepending XPUBLIC from interface file because Interface
53 | Builder was not able to handle it
54 | - IBAction methods are always considered public (I don't see a case where
55 | they are not
56 | - Static methods are also considered public
57 |
58 | 0.7 (2010-02-07)
59 | - Fix for Apple Script calls
60 | - Handle comment correctly
61 | - New FORCE_METHODS setting
62 |
63 | 0.8 (2010-02-15)
64 | - Moved @synthesize to the end of files
65 | - Moved @property to the end of files
66 |
67 | 0.9 (2010-03-14)
68 | - Methods which start with 'initWith' are considered public
69 | - Added XPROPERTY(..) for individual property definitions, e.g.
70 | XPROPERTY(readonly) id test;
71 | - Removed XATOMIC and XREADONLY
72 | - Code cleanup
73 |
74 | 0.10
75 | - Now also works as build script
76 | - Only handle files if different file times
77 | - Only change files that contain X... macros
78 | - Nonatomic can be turned of as default
79 | - Strip trailing spaces
80 | - Multiple file and path arguments
81 | - Added XDELEGATE
82 |
83 | 0.11 (2010-09-07)
84 | - Fix: Can handle missing spaces around asterisk
85 | - External settings did not work, therefore removed
86 |
87 | 0.12 (2010-09-21)
88 | - Categories support
89 | - Expand environment variables for backup path
90 | - Support for .mm file suffixes
91 | - DEBUG does not write files
92 |
93 | 0.13 (2011-01-08)
94 | - XPROPERTY arguments case sensitive
95 | - Added 'xobjc' marker into @property
96 | - 'XASSIGN BOOL xxx' creates corect 'isXxx' getter
97 |
98 | 0.14 (2012-02-23)
99 | - Xcode 4.3 is picky about the position of XPUBLIC therefore
100 | moved it into the return parameter definition like
101 | - (XPUBLIC void)someMethod
102 | - Empty deallocs will not be written
103 |
104 | TODO:
105 |
106 | - Work with more implementations etc. in one file and match name
107 | => Currently just one implementation per file
108 | - NSCoder support
109 | => Create all needed stuff
110 | - XPRIVATE
111 | => Put them into a category in the implementation file
112 |
113 | """
114 |
115 | __version__ = "0.14"
116 |
117 | import re
118 | import os
119 | import os.path
120 | import shutil
121 | import pprint
122 | import datetime
123 | import subprocess
124 |
125 | ### CONFIG BEGIN
126 |
127 | # !!! PLEASE CHANGE THE FOLLOWING TO YOU NEEDS !!!
128 |
129 | # No backup at all
130 | # BACKUP_FOLDER = None
131 |
132 | # Subfolder of the sources location
133 | # BACKUP_FOLDER = 'BACKUP-XOBJC'
134 |
135 | # All into one absolute path
136 | BACKUP_FOLDER = os.path.expandvars('${HOME}/work/_build/__xobjc_backup')
137 | DEBUG = 0
138 | FORCE_METHODS = False #True
139 | BOOL_WITH_IS_GETTER = True
140 | STRIP_TRAILING_SPACES = True
141 | # NONATOMIC = ""
142 | NONATOMIC = "nonatomic, "
143 |
144 | ### CONFIG END
145 |
146 | rxInterface = re.compile("""
147 | .*?
148 | @interface (?P .*?)
149 | \{
150 | (?P .*? )
151 | \}
152 | (?P .*?)
153 | @end
154 | .*?
155 | """, re.VERBOSE | re.M | re.DOTALL)
156 |
157 | rxInterfaceCat = re.compile("""
158 | .*?
159 | @interface
160 | (?P .*?)
161 | \(
162 | (?P .*?)
163 | \)
164 |
165 | (?P .*?)
166 | @end
167 | .*?
168 | """, re.VERBOSE | re.M | re.DOTALL)
169 | rxImplementation = re.compile("""
170 | .*?
171 | \@implementation\s+(?P[a-zA-Z0-9_]+)
172 | (?P .*?)
173 | \@end
174 | """, re.VERBOSE | re.M | re.DOTALL)
175 |
176 | rxDealloc = re.compile("""
177 | \-\s*\(void\)\s*
178 | dealloc
179 | \s*
180 | \{
181 | (?P .*?)
182 | (\[\s*[^\s]+\s+x?release\s*\]\s*\;\s*)*
183 | \[\s*super\s+dealloc\s*\]\s*\;\s*
184 | \}
185 | """, re.VERBOSE | re.M | re.DOTALL)
186 |
187 | rxViewDidUnload = re.compile("""
188 | \-\s*\(void\)\s*
189 | viewDidUnload
190 | \s*
191 | \{
192 | (?P [^\}]*?)
193 | \}
194 | """, re.VERBOSE | re.M | re.DOTALL)
195 |
196 | rxViewDidUnloadBody = re.compile("""
197 | \[\s*super\s+viewDidUnload\s*\]\s*\;
198 | |
199 | self\.[a-zA-Z0-9_]+ \s* \= \s* (xnil|XNIL) \s* \;
200 | """, re.VERBOSE | re.M | re.DOTALL)
201 |
202 | rxVariables = re.compile("""
203 | (XCOPY | XASSIGN | XRETAIN | XIBOUTLET | XDELEGATE | XPROPERTY\(.*?\))
204 | \s+
205 | ([^\s]*)
206 | ((
207 | (?:
208 | \s*\*
209 | |
210 | \s
211 | )
212 | \s*
213 | [a-zA-Z0-9_]+
214 | \s*
215 | \,?
216 | \s*
217 | )+)
218 | \;
219 | """, re.VERBOSE | re.M | re.DOTALL)
220 |
221 | rxProperty = re.compile("""
222 | \@property
223 |
224 | \s*
225 |
226 | (
227 | \(
228 | .*?
229 | \)
230 | )?
231 |
232 | \s*
233 |
234 | ([a-zA-Z0-9_][a-zA-Z0-9_\<\>]*)
235 |
236 | \s+
237 |
238 | ((
239 | \*?
240 | \s*
241 | [a-zA-Z0-9_]+
242 | \s*
243 | \,?
244 | \s*
245 | )+)
246 | \;
247 |
248 | """, re.VERBOSE | re.M | re.DOTALL)
249 |
250 | rxSynthesize = re.compile("""
251 | \@synthesize
252 | \s+
253 | \w+
254 | \s*
255 | \=?
256 | \s*
257 | \w*
258 | \;
259 | """, re.VERBOSE | re.M | re.DOTALL)
260 |
261 | rxLeadingUnderscore = re.compile("(\s*\*?\s*)_(.+)")
262 |
263 | rxMethod = re.compile("""
264 | (?P
265 | (XPUBLIC)?
266 | )
267 | \s*
268 | (?P
269 | [\-\+]
270 | \s*
271 | \([^\)]+\)
272 | \s*
273 | [a-zA-Z_]
274 | [^\{\=]+?
275 | )
276 | \{
277 |
278 | """, re.VERBOSE | re.M | re.DOTALL)
279 |
280 | #rxInstance = re.compile("""
281 | # XINSTANCE([^\s]+)
282 | #""", re.VERBOSE | re.M | re.DOTALL)
283 |
284 | rxComment = re.compile("""
285 | (
286 | \/\*.*?\*\/
287 | |
288 | ^\s*\/\/.*?$
289 | )
290 | """, re.VERBOSE | re.M | re.DOTALL)
291 |
292 | rxInitMethod = re.compile("""
293 | \-\s*(.*?)\s*initWith.*
294 | """, re.VERBOSE | re.M | re.DOTALL)
295 |
296 | def mySorted(v, **k):
297 | return v
298 | # return sorted(v, **k)
299 |
300 | class Module:
301 |
302 | def __init__(filename):
303 | self.base = filename[:filename.rfind(".")]
304 | self.h = self.base + '.h'
305 | self.m = self.baee = ".m"
306 |
307 | def stripComments(value):
308 | #if DEBUG:
309 | # for c in rxComment.findall(value):
310 | # print c
311 | return rxComment.sub('', value)
312 |
313 | def extractVariables(data):
314 | return [x.strip() for x in data.strip().split(",")]
315 |
316 | def insertString(base, pos, new):
317 | return base[:pos] + new + base[pos:]
318 |
319 | def analyze(hdata, mdata):
320 |
321 | ### HEADER
322 |
323 | vars = dict()
324 |
325 | propBlock = []
326 | viewdidunload = []
327 | dealloc = []
328 | block = []
329 | isCategory = 0
330 |
331 | interfaceMatch = rxInterface.match(hdata)
332 | if not interfaceMatch:
333 | interfaceMatch = rxInterfaceCat.match(hdata)
334 | if not interfaceMatch:
335 | return None, None
336 | else:
337 | isCategory = 1
338 |
339 | if not isCategory:
340 | varblock = interfaceMatch.group("varblock")
341 | varblock = stripComments(varblock.strip())
342 |
343 | # Collect variable definitions
344 | for mv in rxVariables.finditer(varblock):
345 | mode, type_, names, names_ = mv.groups()
346 | for vname in extractVariables(names):
347 | vars[''.join(vname.split())] = (mode.lower(), type_, mode)
348 |
349 | # Remove @properties completely from interface
350 | properties = interfaceMatch.group("properties")
351 |
352 | if isCategory:
353 | for mpp in rxProperty.finditer(properties):
354 | propBlock.append(mpp.group(0))
355 |
356 | if not isCategory:
357 | properties = rxProperty.sub('', properties).lstrip()
358 |
359 | # Create @properties
360 | for vname in mySorted(vars.keys(), key=lambda k:k.strip('*').strip('_')):
361 | mode, type_, origMode = vars[vname]
362 |
363 | iboutlet = 0
364 | star = '*' if vname.startswith('*') else ''
365 | name = vname.lstrip('*') # Withoout leading *
366 | pvname = name # Without underscore
367 |
368 | # Google compatible synthesize
369 | if name.endswith('_'):
370 | pvname = name[:-1]
371 | block.append("@synthesize %s = %s;" % (pvname, name))
372 | elif name.startswith('_'):
373 | pvname = name[1:]
374 | block.append("@synthesize %s = %s;" % (pvname, name))
375 | else:
376 | block.append("@synthesize %s;" % (name))
377 |
378 | # Properties
379 | propMarker = "xobjc "
380 | if mode == 'xiboutlet':
381 | iboutlet = 1
382 | mode = "retain"
383 | type_ = "IBOutlet %s" % type_
384 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
385 | elif mode == 'xdelegate':
386 | iboutlet = 1
387 | mode = "assign"
388 | type_ = "IBOutlet %s" % type_
389 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
390 | elif mode.startswith('xproperty('):
391 | # XXX Iboutlet
392 | pattr = origMode.strip()[10:-1]
393 | propBlock.append("@property (%s%s) %s %s%s;" % (propMarker, pattr, type_, star, pvname))
394 | mode = 'assign'
395 | pattrlist = [x.strip().lower() for x in pattr.split(',')]
396 | if 'retain' in pattrlist or 'copy' in pattrlist:
397 | mode = 'retain'
398 | else:
399 | mode = mode[1:]
400 | if BOOL_WITH_IS_GETTER and type_ == "BOOL":
401 | mode = "getter=is%s%s" % (pvname[0].capitalize(), pvname[1:])
402 | propBlock.append("@property (%s%s%s) %s %s%s;" % (propMarker, NONATOMIC, mode, type_, star, pvname))
403 |
404 | # print mode
405 |
406 | # Release stuff
407 | if mode in ('retain', 'copy'):
408 | dealloc.append(" [%s xrelease];" % name)
409 |
410 | if iboutlet:
411 | viewdidunload.append(" self.%s = xnil;" % pvname)
412 |
413 | # print viewdidunload
414 |
415 | propBlock = "\n".join(propBlock)
416 |
417 | ### MODULE
418 |
419 | # Find implementation blinterfaceMatch
420 | implementationMatch = rxImplementation.match(mdata)
421 | impName = implementationMatch.group('name')
422 |
423 | #if DEBUG and implementationMatch:
424 | # print "Implementation", implementationMatch.groups()
425 |
426 | # Replace @synthesize block
427 | body = implementationMatch.group("body")
428 |
429 | if not isCategory:
430 | body = rxSynthesize.sub('', body).strip()
431 | block = "\n".join(block) + '\n\n'
432 |
433 | # Update 'dealloc'
434 | md = rxDealloc.search(body)
435 | if md:
436 | deallocbody = md.group("deallocbody").strip()
437 | if deallocbody or dealloc:
438 | # deallocbody = rxRelease.sub('', md.group("deallocbody")).strip()
439 | if deallocbody:
440 | deallocbody = " " + deallocbody + "\n\n"
441 | newdealloc = ("- (void)dealloc { "
442 | + ("\n" + deallocbody).rstrip()
443 | + ("\n" + "\n".join(mySorted(dealloc))).rstrip()
444 | + "\n [super dealloc];\n}")
445 | body = rxDealloc.sub(newdealloc, body)
446 | else:
447 | if dealloc:
448 | newdealloc = "- (void)dealloc {\n" + "\n".join(mySorted(dealloc)) + "\n [super dealloc];\n}"
449 | body += "\n\n" + newdealloc
450 |
451 | # Update 'viewDidUnload' (iPhone and iPad only)
452 | md = rxViewDidUnload.search(body)
453 | if md:
454 | viewdidunloadbody = rxViewDidUnloadBody.sub('', md.group("viewdidunloadbody")).strip()
455 | if viewdidunloadbody:
456 | viewdidunloadbody = "\n " + viewdidunloadbody + "\n\n"
457 | newviewdidunloadbody = (
458 | "- (void)viewDidUnload {\n [super viewDidUnload];\n"
459 | + (" " + viewdidunloadbody.strip()).rstrip()
460 | + ("\n" + "\n".join(mySorted(viewdidunload))).rstrip()
461 | + "\n}")
462 | body = rxViewDidUnload.sub(newviewdidunloadbody, body)
463 |
464 | ### METHODS
465 | mDefs = []
466 | xpub = 0
467 | bodyStripped = stripComments(body)
468 |
469 | for mMethod in rxMethod.finditer(bodyStripped):
470 | mName = mMethod.group('name').strip()
471 | # if mMethod.group("comment"):
472 | # mName = "\n" + mMethod.group("comment").strip() + "\n" + mName
473 | #if DEBUG:
474 |
475 | print mName, mMethod.groups()
476 | if (mMethod.group('kind') == 'XPUBLIC'):
477 | xpub += 1
478 | mDefs.append(mName + ';')
479 | elif mName.startswith("+") or mName.lstrip('-').lstrip().startswith("(IBAction)"):
480 | mDefs.append(mName + ';')
481 | elif mName.lstrip('-').lstrip().startswith("(XPUBLIC "):
482 | mDefs.append(mName.replace("XPUBLIC ", "") + ';')
483 | xpub += 1
484 | elif rxInitMethod.match(mName):
485 | mDefs.append(mName + ';')
486 |
487 | ### XINSTANCE
488 | #mdi = rxInstance.search(bodyStripped)
489 | #if mdi:
490 | # xpub += 1
491 | # mDefs.append("+ (id)instance;")
492 |
493 | # If no XPUBLIC was defined don't replace old stuff
494 | if mDefs or FORCE_METHODS:
495 | # mDefs = "\n".join(mySorted(mDefs)) + '\n\n'
496 | mDefs = "\n".join(mDefs) + '\n\n'
497 | else:
498 | mDefs = properties
499 |
500 | ### RESULT
501 |
502 | if isCategory:
503 |
504 | hdata = (hdata[:interfaceMatch.start("properties")]
505 | + ('\n\n' + propBlock).rstrip()
506 | + ('\n\n' + mDefs).rstrip()
507 | + '\n\n' + hdata[interfaceMatch.end("properties"):])
508 |
509 | else:
510 |
511 | hdata = (hdata[:interfaceMatch.start("properties")]
512 | + ('\n\n' + mDefs).rstrip()
513 | + ('\n\n' + propBlock).rstrip()
514 | + '\n\n' + hdata[interfaceMatch.end("properties"):])
515 |
516 | mdata = (mdata[:implementationMatch.start('body')]
517 | + ('\n\n' + body).rstrip()
518 | + ('\n\n' + block).rstrip()
519 | + '\n\n' + mdata[implementationMatch.end('body'):])
520 |
521 | # Did something change?
522 | if xpub or propBlock:
523 | return hdata, mdata
524 |
525 | return None, None
526 |
527 | def modifyFiles(filename):
528 |
529 | # Calculate basic filenames
530 | base = os.path.normpath(os.path.abspath(filename))
531 | folder = os.path.dirname(base)
532 | filePart = os.path.basename(base)
533 |
534 | if filePart == "main.m":
535 | # print "File %r will not be modified" % filePart
536 | return False
537 |
538 | hfile = filename[:filename.rfind(".")] + '.h'
539 | mfile = filename[:filename.rfind(".")] + '.m'
540 |
541 | # Check if files exist
542 | if not os.path.isfile(hfile):
543 | # print "File %r does not exist" % hfile
544 | return False
545 | if not os.path.isfile(mfile):
546 | # print "File %r does not exist" % hfile
547 | mfile = filename[:filename.rfind(".")] + '.mm'
548 | if not os.path.isfile(mfile):
549 | return False
550 |
551 | htime = os.stat(hfile).st_mtime
552 | mtime = os.stat(mfile).st_mtime
553 |
554 | if htime == mtime:
555 | # print "No update needed"
556 | return False
557 |
558 | # Handle and modify files
559 | hsrc = open(hfile).read()
560 | msrc = open(mfile).read()
561 | if ("noxobjc" in hsrc.lower()) or ("noxobjc" in msrc.lower()):
562 | # print "File ignored"
563 | return False
564 |
565 | # Handle and modify files
566 | hdata, mdata = analyze(
567 | hsrc,
568 | msrc)
569 |
570 | if not (hdata and mdata):
571 | return False
572 |
573 | # Backup files
574 | if BACKUP_FOLDER:
575 | backupFolder = os.path.join(
576 | folder,
577 | BACKUP_FOLDER,
578 | 'backup-' + datetime.datetime.today().strftime("%Y%m%d-%H%M%S"))
579 | if not os.path.isdir(backupFolder):
580 | os.makedirs(backupFolder)
581 | shutil.copyfile(hfile, os.path.join(backupFolder, filePart[:-2] + '.h'))
582 | shutil.copyfile(mfile, os.path.join(backupFolder, filePart[:-2] + '.m'))
583 | # print "Created backup of files in %r" % backupFolder
584 |
585 |
586 | if STRIP_TRAILING_SPACES:
587 | hdata = "\n".join([l.rstrip() for l in hdata.splitlines()])
588 | mdata = "\n".join([l.rstrip() for l in mdata.splitlines()])
589 |
590 | if DEBUG:
591 | print "=" * 80
592 | print hfile
593 | print "=" * 80
594 | print hdata
595 | print "=" * 80
596 | print mfile
597 | print "=" * 80
598 | print mdata
599 |
600 | if not DEBUG:
601 | f = open(hfile, 'w')
602 | f.write(hdata)
603 | f.close()
604 |
605 | f = open(mfile, 'w')
606 | f.write(mdata)
607 | f.close()
608 |
609 | # Same file time
610 | subprocess.call(['touch', hfile, mfile])
611 |
612 | #print "Modified %r" % hfile
613 | #print "Modified %r" % mfile
614 |
615 | return True
616 |
617 | def xcodeReload():
618 | # Trick to reload files in XCode
619 | # Bug workaround for SL, see http://kb2.adobe.com/cps/516/cpsid_51615.html
620 | # print "XCode refresh"
621 | # subprocess.call(['arch', '-i386', 'osascript', '-e', 'activate application "Finder"\nactivate application "XCode"'])
622 | pass
623 |
624 | def callAppleScript(script, input=None):
625 | import StringIO
626 | if not input:
627 | return subprocess.check_output(
628 | ['osascript', '-e', script],
629 | stderr=subprocess.STDOUT)
630 |
631 | p = subprocess.Popen(['osascript', '-e', script], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
632 | p.stdin.write(input)
633 | ret = p.communicate()[0]
634 | p.stdin.close()
635 | return ret
636 |
637 | def callGrowl(msg):
638 | callAppleScript("""
639 | tell application "GrowlHelperApp"
640 | set the allNotificationsList to {"XObjC"}
641 | set the enabledNotificationsList to {"XObjC"}
642 | register as application "XObjC" all notifications allNotificationsList default notifications enabledNotificationsList icon of application "XCode"
643 | notify with name "XObjC" title "XCode" description "%s" application name "XObjC"
644 | end tell
645 | """ % repr(msg)[1:-1])
646 |
647 | OUT = []
648 |
649 | def out(*a):
650 | OUT.append(" ".join([str(v) for v in a]))
651 |
652 | def main():
653 | filenames = callAppleScript("""
654 | tell application id "com.apple.dt.Xcode"
655 | return path of source documents
656 | end tell
657 | """)
658 |
659 | if not filenames:
660 | print "Nothing to do"
661 |
662 | filenames = [n.strip() for n in filenames.split(',')]
663 |
664 | modified = False
665 | for filename in filenames:
666 |
667 | filename = os.path.abspath(filename)
668 | # print "Analyze %s" % filename
669 |
670 | mfiles = [filename]
671 |
672 | # XXX Obsolete?
673 | if os.path.isdir(filename):
674 | for root, dirs, files in os.walk(filename):
675 | for name in files:
676 | if (BACKUP_FOLDER not in root) and name.endswith(".m"):
677 | mfiles.append(os.path.join(root, name))
678 |
679 | # print "FILES:"
680 | # print "\n".join(mfiles)
681 |
682 | # elif srcroot:
683 | # files = glob.glob("Classes/*.m")
684 |
685 | if mfiles:
686 |
687 | for fn in mfiles:
688 | if modifyFiles(fn):
689 | out("Modified: %s" % os.path.basename(fn))
690 | modified = True
691 |
692 | if modified:
693 | # xcodeReload()
694 | pass
695 | else:
696 | out("No modifications needed")
697 |
698 | s = '\n'.join(OUT)
699 | if s:
700 | print s
701 | callGrowl(s)
702 |
703 | if __name__ == "__main__":
704 | main()
705 |
--------------------------------------------------------------------------------
/xpublicmove.py:
--------------------------------------------------------------------------------
1 | """
2 | Transforms:
3 |
4 | XPUBLIC
5 | - (void)bla {
6 |
7 | Into:
8 |
9 | - (XPUBLIC void)bla {
10 |
11 | For Xcode 4.3 compatibility
12 | """
13 |
14 | import glob
15 | import re
16 |
17 | rx = re.compile("\-\s*\(")
18 | files = (
19 | # Add you implementation files here
20 | # glob.glob("/Users/SOMEUSER/work/SOMETPATH/*.m")
21 | )
22 |
23 | for name in files:
24 | print name
25 | next = 0
26 | lines = []
27 | for line in open(name, "r").readlines():
28 | if line.startswith("XPUBLIC"):
29 | next = 1
30 | continue
31 | # print line
32 | if next and line.startswith("-"):
33 | line = rx.sub("- (XPUBLIC ", line)
34 | print " ", line,
35 | lines += line.rstrip() + '\n'
36 | next = 0;
37 |
38 | open(name, "w").writelines(lines)
39 |
--------------------------------------------------------------------------------