/
nuke
executable file
·755 lines (658 loc) · 37 KB
/
nuke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
#!/usr/bin/env nush
#
# @file nuke
# The Nu software construction tool.
#
# @copyright Copyright (c) 2007 Tim Burks, Neon Design Technology, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
;; basic declarations that allow nuke to run in mininush
(global NO 0)
(global YES 1)
(set exit (NuBridgedFunction functionWithName:"exit" signature:"vi"))
(function SH (command)
(puts "nuke: #{command}")
(set result (system command))
(if result
(puts "nuke: terminating on command error (return code #{result})")
(exit result)))
(class NSString
;; Change the extension at the end of a file name to a new specified value.
(imethod (id) stringByReplacingPathExtensionWith:(id) newExtension is
((self stringByDeletingPathExtension) stringByAppendingPathExtension:newExtension))
;; Get the directory name component of a file name.
(imethod (id) dirName is
(set components (self componentsSeparatedByString:"/"))
(set result ((NSMutableString alloc) init))
((- (components count) 1) times:
(do (n)
(result appendString:(components objectAtIndex:n))
(result appendString:"/")))
result)
;; Get the file name component of a file name.
(imethod (id) fileName is
(set components (self componentsSeparatedByString:"/"))
(components objectAtIndex:(- (components count) 1))))
(class NSDictionary
;; Create a dictionary from a list.
(cmethod (id) dictionaryWithList: (id) l is
(set dict ((NSMutableDictionary alloc) init))
(l eachPair: (do (key object) (dict setObject:object forKey:key)))
dict))
(class NSArray
;; Create an array from a list.
(cmethod (id) arrayWithList:(id) list is
(set l ((NSMutableArray alloc) init))
(list each: (do (item) (if item (l addObject:item))))
l)
;; Join the non-null members of an array into a string with array elements separated by spaces.
(imethod (id) join is
((self select:(do (x) x)) componentsJoinedByString:" "))
;; Get the element of an array with the maximum value (as obtained using the specified block).
(imethod (id) maximum:(id)block from:(id)initial is
(self reduce:
(do (max d)
(set value (block d))
(if (> value max) (then value) (else max)))
from: initial)))
(class NuCell
;; Join the elements of a list into a string with list elements separated by spaces.
(imethod (id) join is
((NSArray arrayWithList:self) join)))
;; creates a named directory if it does not already exist
(function make-directory (dir)
(set components (dir componentsSeparatedByString:"/"))
((components count) times:
(do (i)
(set partial
((components subarrayWithRange:(list 0 (+ i 1))) componentsJoinedByString:"/"))
(unless (eq (NSFileManager directoryExistsNamed:partial) YES)
(SH "mkdir '#{partial}'"))))
0)
;; returns an array of filenames matching a given pattern
(function filelist (pattern)
(set regex (NuRegex regexWithPattern:pattern))
(set results ((NSMutableSet alloc) init))
(let (enumerator ((NSFileManager defaultManager) enumeratorAtPath:"."))
(while (set filename (enumerator nextObject))
(if (regex findInString:(filename stringValue))
(results addObject:filename))))
results)
;; we need support for optional arguments with default values...
(function filelistWithRoot (pattern root)
(set regex (NuRegex regexWithPattern:pattern))
(set results ((NSMutableSet alloc) init))
(let (enumerator ((NSFileManager defaultManager) enumeratorAtPath:root))
(while (set filename (enumerator nextObject))
(if (regex findInString:(filename stringValue))
(results addObject:filename))))
results)
;; @abstract A task description, complete with action and dependency information.
;; @discussion NukeTasks are used to represent tasks in nuke,
;; the Nu build tool. A NukeTask may be associated with a file
;; or may represent an abstract set of operations. Tasks have
;; dependencies; each dependency must be satisfied before
;; the task action is attempted. In nuke, tasks are created
;; using the <b>task</b> and <b>file</b> macros.
(class NukeTask is NSObject
(ivar (id) name) ;; the name of the task
(ivar (id) dependencies) ;; an array of references to other tasks
(ivar (id) action) ;; a block which performs the necessary build action
(ivar (int) isFile) ;; if nonzero, task is a file task
(ivar (id) result) ;; the result of executing the task
;; @discussion Create a task with a specified name.
(imethod (id) initWithName:(id) name is
(super init)
(set @name name)
(set @isFile 1)
(set @dependencies ((NSMutableArray alloc) init))
self)
;; Get a task's name.
(imethod (id) name is @name)
;; Get the array containing the task's dependencies.
(imethod (id) dependencies is @dependencies)
;; Set the action of a task to a specified block.
(imethod (void) setAction:(id) action is (set @action action))
;; Indicate whether or not a task is a file creation task.
(imethod (void) setIsFile:(int) f is (set @isFile f))
;; Determine whether or not a task is a file creation task.
(imethod (int) isFile is @isFile)
;; Get a time stamp for the target of a task.
(imethod (id) timestamp is
(set date (NSFileManager creationTimeForFileNamed:@name))
(cond ((eq @isFile 0) 0)
(date ((NSFileManager modificationTimeForFileNamed:@name) timeIntervalSinceReferenceDate))
(else 0)))
;; Attempt to update a task, first by updating all its dependencies,
;; then, if no errors occurred, by performing the the action of a task.
(imethod (id) update is
(unless @result
;; first update all dependencies
(set @result (@dependencies reduceLeft: (do (r d) (+ r (d update))) from:0))
;; continue only if there were no errors
(if (eq @result 0)
;; then get the largest timestamp -- it represents the newest dependency
(set dependency-timestamp
(@dependencies maximum:(do (d) (d timestamp)) from:0))
;; if the largest timestamp is greater than or equal to this task's timestamp, perform the task's action
(if (or (eq (self timestamp) 0) (> dependency-timestamp (self timestamp)))
;;(puts "task #{@name} dependency #{dependency-timestamp} self #{(self timestamp)}")
(set @result (@action self))
(unless @result (set @result 0))
nil))
(if $verbose (puts "task #{@name} result is #{@result}")))
@result))
;; do not use this directly. It is common code extracted from the file and task macros.
(macro task-helper
(set __dependency nil)
(set __action nil)
(set __taskName (eval (car margs)))
(set __cursor (cdr margs))
;; get dependencies
(if (and __cursor (eq (car __cursor) (quote =>)))
(set __cursor (cdr __cursor))
(while (and __cursor (!= (car __cursor) (quote is)))
(set __dependency (eval (car __cursor)))
(self addDependency:__taskName on:__dependency)
(set __cursor (cdr __cursor))))
;; get action
(if (and __cursor (eq (car __cursor) (quote is)))
(set __action (cdr __cursor))
(set __block (eval (append '(do (target)) __action)))
(self addTask:__taskName action:__block))
;; return the name of the task
__taskName)
;; use this to define tasks that create files.
(macro file
((self taskNamed: (eval (cons (quote task-helper) margs))) setIsFile:1))
;; use this to define tasks that DON'T create files.
(macro task
((self taskNamed: (eval (cons (quote task-helper) margs))) setIsFile:0))
;; helper that finds momc, the datamodel compiler
(function momc-path ()
(set momc nil)
('( "/Library/Application Support/Apple/Developer Tools/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/momc"
"/Developer/Library/Xcode/Plug-ins/XDCoreDataModel.xdplugin/Contents/Resources/momc")
each:
(do (momc-path)
(if (NSFileManager fileExistsNamed:momc-path)
(set momc (momc-path replaceString:" " withString:-"\ ")))))
(unless momc (NSException raise:@"NukeError" format:@"Can't find momc (data model compiler)."))
momc)
;; use this to create all the compilation tasks for the files in the @c_files and @m_files collections
(macro compilation-tasks
(unless @cc (set @cc "gcc"))
(unless @cflags (set @cflags "-g"))
(unless @mflags (set @mflags "-fobjc-exceptions"))
(unless @includes (set @includes ""))
(unless (and @arch (@arch length))
(set @arch (list (NSString stringWithShellCommand:"arch"))))
(@arch each: (do (architecture) (system "mkdir -p build/#{architecture}")))
(unless @ldflags ;; not for compilation, but common across all builds
(set @ldflags ((@frameworks map: (do (framework) " -framework #{framework}")) join)))
;; compile c files
(set @c_objects (NSMutableDictionary dictionary))
(@arch each: (do (architecture) (@c_objects setObject:(NSMutableArray array) forKey:architecture)))
(@c_files each:
(do (sourceName)
(@arch each:
(do (architecture)
(set objectName "build/#{architecture}/")
(objectName appendString:((sourceName fileName) stringByReplacingPathExtensionWith:"o"))
((@c_objects objectForKey:architecture) addObject: objectName)
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file objectName => sourceName is
(SH "#{@cc} #{@cflags} #{archflags} #{@includes} -c -o #{(target name)} #{sourceName}"))))))
;; compile objc files
(set @m_objects (NSMutableDictionary dictionary))
(@arch each: (do (architecture) (@m_objects setObject:(NSMutableArray array) forKey:architecture)))
(@m_files each:
(do (sourceName)
(@arch each:
(do (architecture)
(set objectName "build/#{architecture}/")
(objectName appendString:((sourceName fileName) stringByReplacingPathExtensionWith:"o"))
((@m_objects objectForKey:architecture) addObject: objectName)
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file objectName => sourceName is
(SH "#{@cc} #{@cflags} #{@mflags} #{archflags} #{@includes} -c -o #{(target name)} #{sourceName}"))))))
;(puts (@c_objects description))
;(puts (@m_objects description))
;; compile datamodels
(@datamodels each:
(do (model)
(set modelName ((model componentsSeparatedByString:".") objectAtIndex:0))
(file "#{modelName}.mom" => "#{modelName}.xcdatamodel" is
(SH "#{(momc-path)} #{modelName}.xcdatamodel #{modelName}.mom"))))
;; cleanup
(task "clean" is
(system "rm -rf build")))
;; use this to create all the linking and assembly tasks to build a Cocoa application
(macro application-tasks
;; DEFAULT: if no application name is specified, use something dumb and complain.
(unless @application
(set @application "Untitled")
(NSLog "Please name your application by setting @application in your Nukefile"))
;; DEFAULT: if no prefix is specified, use the path where nuke is installed.
(unless @prefix
(set @prefix "#{((((NSProcessInfo processInfo) arguments) 0) dirName)}.."))
;; DEFAULT: if no application identifier is specifed, use something dumb and complain.
(unless @application_identifier
(set @application_identifier "nu.programming.untitled")
(NSLog "Please set an @application_identifier in your Nukefile"))
;; DEFAULT: if no icon is specified, use the nu icon for the application.
(unless @application_icon_file (set @application_icon_file "nu.icns"))
(unless @icon_files (set @icon_files (array "#{@prefix}/share/nu/resources/nu.icns")))
;; DEFAULT: if no nib files are specified, use the standard (empty) Nu MainMenu.nib.
(unless @nib_files (set @nib_files (array "#{@prefix}/share/nu/resources/English.lproj/MainMenu.nib")))
;; DEFAULT: if no creator code is specified, use "????"
(unless @application_creator_code (set @application_creator_code "????"))
;; app directory tasks
(set @application_dir "#{@application}.app")
(set @application_contents_dir "#{@application_dir}/Contents")
(set @application_executable_dir "#{@application_contents_dir}/MacOS")
(set @application_resource_dir "#{@application_contents_dir}/Resources")
(set @application_resource_localized_dir "#{@application_resource_dir}/English.lproj")
;; make the application directory structure
((list @application_dir
@application_contents_dir
@application_executable_dir
@application_resource_dir
@application_resource_localized_dir)
each: (do (dir) (file dir is (make-directory (target name)))))
;; application executable
(set @application_executable_name "#{@application_executable_dir}/#{@application}")
(if (or (and @c_objects (@c_objects count) (((@c_objects allValues) objectAtIndex:0) count))
(and @m_objects (@m_objects count) (((@m_objects allValues) objectAtIndex:0) count)))
(then
;; application architecture-specific executable
(set @application_executables (NSMutableArray array))
(@arch each:
(do (architecture)
(set application_executable "build/#{architecture}/application-#{@application}")
(@application_executables addObject:application_executable)
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file application_executable => (@c_objects objectForKey:architecture) (@m_objects objectForKey:architecture) is
(set command "#{@cc} #{((@c_objects objectForKey:architecture) join)} #{((@m_objects objectForKey:architecture) join)} #{archflags} #{@cflags} #{@ldflags} -o '#{(target name)}'")
(SH command))))
;; application fat executable
(file @application_executable_name => @application_executable_dir @application_executables is
(if (eq (uname) "Darwin")
(then (set command "lipo -create #{(@application_executables join)} -output '#{@application_executable_name}'"))
(else (set command "cp '#{(@application_executables objectAtIndex:0)}' '#{@application_executable_name}'")))
(SH command)))
(else
(file @application_executable_name => @application_executable_dir is
(SH "cp '#{@prefix}/bin/nush' '#{(target name)}'"))))
;; copy application_resources into the application
(task "application_resources" => @application_resource_dir)
((list @nu_files @icon_files) each:
(do (l)
(l each:
(do (f)
(set baseName ((f componentsSeparatedByString:"/") lastObject))
(set targetFile "#{@application_resource_dir}/#{baseName}")
(file targetFile => f @application_resource_dir is
(SH "cp '#{f}' '#{targetFile}'"))
(task "application_resources" => targetFile)))))
((list @nib_files @resources) each:
(do (l)
(l each:
(do (f)
(set g ((f componentsSeparatedByString:"/") lastObject))
(file "#{@application_resource_localized_dir}/#{g}" => f @application_resource_localized_dir is
(SH "cp -R '#{f}' '#{((target name) dirName)}'")
0)
(task "application_resources" => "#{@application_resource_localized_dir}/#{g}")))))
;; copy datamodels into the application
(@datamodels each:
(do (model)
(set modelName ((model componentsSeparatedByString:".") objectAtIndex:0))
(task "application_#{modelName}" => "#{modelName}.mom" @application_resource_dir is
(SH "cp '#{modelName}.mom' '#{@application_resource_dir}/#{(modelName lastPathComponent)}.mom'"))
(task "application" => "application_#{modelName}")))
;; create the application_infoplist
(set application_infoplist "#{@application_contents_dir}/Info.plist")
(file application_infoplist => @application_contents_dir is
(set info (NSDictionary dictionaryWithList:
(list "CFBundleDevelopmentRegion" "English"
"CFBundleExecutable" @application
"CFBundleIconFile" @application_icon_file
"CFBundleIdentifier" @application_identifier
"CFBundleInfoDictionaryVersion" "6.0"
"CFBundleName" @application
"CFBundlePackageType" "APPL"
"CFBundleSignature" @application_creator_code
"CFBundleVersion" "1.0"
"NSMainNibFile" "MainMenu"
"NSHumanReadableCopyright" "Copyright (c) 2007 Tim Burks, Neon Design Technology, Inc."
"NSPrincipalClass" "NSApplication")))
(if @application_help_folder
(info setObject:@application_help_folder forKey:"CFBundleHelpBookFolder")
(info setObject:@application_help_folder forKey:"CFBundleHelpBookName"))
(if @info (info addEntriesFromDictionary: @info))
(info writeToFile:(target name) atomically:NO)
0)
;; write the application_pkginfo
(unless @creator_code (set @creator_code "????"))
(set application_pkginfo "#{@application_contents_dir}/PkgInfo")
(file application_pkginfo => @application_contents_dir is
(SH "echo -n 'APPL#{@creator_code}' > '#{(target name)}'"))
(task "application" => @application_executable_name "application_resources" application_infoplist application_pkginfo)
(task "run" => "application" is
(SH "open '#{@application_dir}'"))
(task "debug" => "application" is
(SH "'#{@application_dir}/Contents/MacOS/#{@application}'"))
(task "gdb" => "application" is
(SH "gdb '#{@application_dir}/Contents/MacOS/#{@application}'"))
(task "clobber" => "clean" is
(system "rm -rf '#{@application_dir}'"))
; Build a disk image for distributing the application.
(task "application_image" => "application" is
(system "rm -rf '#{@application}.dmg' dmg")
(system "mkdir dmg; cp -Rp '#{@application}.app' dmg")
(system "hdiutil create -srcdir dmg '#{@application}.dmg' -volname '#{@application}'")
(system "rm -rf dmg")))
;; use this to create all the linking and assembly tasks to build a Cocoa framework
(macro framework-tasks
(unless (and @arch (@arch length))
(set @arch (list (NSString stringWithShellCommand:"arch"))))
;; framework directory tasks
(set @framework_dir "#{@framework}.framework")
(set @framework_versions_dir "#{@framework_dir}/Versions")
(set @framework_contents_dir "#{@framework_versions_dir}/A")
(set @framework_headers_dir "#{@framework_contents_dir}/Headers")
(set @framework_resource_dir "#{@framework_contents_dir}/Resources")
(set @framework_resource_localized_dir "#{@framework_resource_dir}/English.lproj")
((list @framework_dir
@framework_versions_dir
@framework_contents_dir
@framework_headers_dir
@framework_resource_dir
@framework_resource_localized_dir)
each: (do (dir) (file dir is (make-directory (target name)))))
(set @initflags (if @framework_initializer
(then "-Wl,-init -Wl,_#{@framework_initializer}")
(else "")))
;; framework architecture-specific executable
(set @framework_executables (NSMutableArray array))
(@arch each:
(do (architecture)
(set framework_executable "build/#{architecture}/framework-#{@framework}")
(@framework_executables addObject:framework_executable)
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file framework_executable => (@c_objects objectForKey:architecture) (@m_objects objectForKey:architecture) is
(set command "#{@cc} #{((@c_objects objectForKey:architecture) join)} #{((@m_objects objectForKey:architecture) join)} #{archflags} #{@cflags} #{@ldflags} #{@initflags} -install_name #{@framework_executable_name} -dynamiclib -o '#{(target name)}'")
(SH command))))
;; framework fat executable
(set @framework_executable_name "#{@framework_contents_dir}/#{@framework}")
(file @framework_executable_name => @framework_contents_dir @framework_executables is
(if (eq (uname) "Darwin")
(then (set command "lipo -create #{(@framework_executables join)} -output '#{@framework_executable_name}'"))
(else (set command "cp '#{(@framework_executables objectAtIndex:0)}' '#{@framework_executable_name}'")))
(SH command))
;; framework_resources
(task "framework_resources" => @framework_resource_dir @framework_resource_localized_dir)
((list @nu_files @resource_files) each:
(do (l)
(l each:
(do (f)
(set baseName ((f componentsSeparatedByString:"/") lastObject))
(set targetFile "#{@framework_resource_dir}/#{baseName}")
(file targetFile => f @framework_resource_dir is
(SH "cp '#{f}' '#{targetFile}'"))
(task "framework_resources" => targetFile)))))
(@nib_files each:
(do (f)
(set g ((f componentsSeparatedByString:"/") lastObject))
(file "#{@framework_resource_localized_dir}/#{g}" => f @framework_resource_localized_dir is
(SH "cp -R '#{f}' '#{((target name) dirName)}'")
0)
(task "framework_resources" => "#{@framework_resource_localized_dir}/#{g}")))
;; copy datamodels into the framework
(@datamodels each:
(do (model)
(set modelName ((model componentsSeparatedByString:".") objectAtIndex:0))
(task "framework_#{modelName}" => "#{modelName}.mom" @framework_resource_dir is
(SH "cp '#{modelName}.mom' '#{@framework_resource_dir}/#{(modelName lastPathComponent)}.mom'"))
(task "framework" => "framework_#{modelName}")))
;; framework_infoplist
(set framework_infoplist "#{@framework_resource_dir}/Info.plist")
(file framework_infoplist => @framework_resource_dir is
(set info (NSDictionary dictionaryWithList:
(list "CFBundleDevelopmentRegion" "English"
"CFBundleExecutable" @framework
"CFBundleIdentifier" @framework_identifier
"CFBundleGetInfoString" "Nu. Copyright (c) 2007 Tim Burks, Neon Design Technology, Inc."
"CFBundleInfoDictionaryVersion" "6.0"
"CFBundleName" @framework
"CFBundlePackageType" "FMWK"
"CFBundleSignature" @framework_creator_code
"CFBundleVersion" "0.1"
"NSHumanReadableCopyright" "Nu. Copyright (c) 2007 Tim Burks, Neon Design Technology, Inc."
)))
(if @info (info addEntriesFromDictionary: @info))
(info writeToFile:(target name) atomically:NO)
0)
;; framework_links
(task "framework_links" => @framework_versions_dir @framework_dir is
(unless (eq 1 (NSFileManager directoryExistsNamed: "#{@framework_versions_dir}/Current"))
(SH "cd #{@framework_versions_dir}; ln -sf A Current"))
(unless (eq 1 (NSFileManager directoryExistsNamed: "#{@framework_dir}/Headers"))
(SH "cd #{@framework_dir}; ln -sf Versions/Current/Headers Headers"))
(unless (eq 1 (NSFileManager directoryExistsNamed: "#{@framework_dir}/Resources"))
(SH "cd #{@framework_dir}; ln -sf Versions/Current/Resources Resources"))
(unless (eq 1 (NSFileManager fileExistsNamed: "#{@framework_dir}/Versions/Current/#{@framework}"))
(SH "cd #{@framework_dir}; ln -sf Versions/Current/#{@framework} #{@framework}"))
0)
(task "framework" => @framework_executable_name @framework_headers_dir "framework_resources" framework_infoplist "framework_links"))
;; use this to create all the linking and assembly tasks to build a Cocoa bundle
(macro bundle-tasks
(unless (and @arch (@arch length))
(set @arch (list (NSString stringWithShellCommand:"arch"))))
;; bundle directory tasks
(set @bundle_dir "#{@bundle}.bundle")
(set @bundle_contents_dir "#{@bundle_dir}/Contents")
(set @bundle_executable_dir "#{@bundle_contents_dir}/MacOS")
(set @bundle_resource_dir "#{@bundle_contents_dir}/Resources")
((list @bundle_dir
@bundle_contents_dir
@bundle_executable_dir
@bundle_resource_dir)
each: (do (dir) (file dir is (make-directory (target name)))))
;; bundle architecture-specific executable
(set @bundle_executables (NSMutableArray array))
(@arch each:
(do (architecture)
(set bundle_executable "build/#{architecture}/bundle-#{@bundle}")
(@bundle_executables addObject:bundle_executable)
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file bundle_executable => (@c_objects objectForKey:architecture) (@m_objects objectForKey:architecture) is
(set command "#{@cc} #{((@c_objects objectForKey:architecture) join)} #{((@m_objects objectForKey:architecture) join)} #{archflags} #{@cflags} #{@ldflags} -bundle -o '#{(target name)}'")
(SH command))))
;; bundle fat executable
(set @bundle_executable_name "#{@bundle_executable_dir}/#{@bundle}")
(file @bundle_executable_name => @bundle_executable_dir @bundle_executables is
(set command "lipo -create #{(@bundle_executables join)} -output '#{@bundle_executable_name}'")
(SH command))
;; bundle_resources
(task "bundle_resources" => @bundle_resource_dir @bundle_resource_localized_dir)
((list @nu_files @resource_files) each:
(do (l)
(l each:
(do (f)
(set baseName ((f componentsSeparatedByString:"/") lastObject))
(set targetFile "#{@bundle_resource_dir}/#{baseName}")
(file targetFile => f @bundle_resource_dir is
(SH "cp '#{f}' '#{targetFile}'"))
(task "bundle_resources" => targetFile)))))
;; bundle_infoplist
(set bundle_infoplist "#{@bundle_contents_dir}/Info.plist")
(file bundle_infoplist => @bundle_contents_dir is
(set bundle_info (NSDictionary dictionaryWithList:
(list "CFBundleDevelopmentRegion" "English"
"CFBundleExecutable" @bundle
"CFBundleIdentifier" @bundle_identifier
"CFBundleInfoDictionaryVersion" "6.0"
"CFBundleName" @bundle
"CFBundlePackageType" "BNDL"
"CFBundleSignature" @bundle_creator_code
"CFBundleVersion" "0.1")))
(if @bundle_info (bundle_info addEntriesFromDictionary: @info))
(bundle_info writeToFile:(target name) atomically:NO)
0)
;; bundle_pkginfo
(unless @bundle_creator_code (set @bundle_creator_code "????"))
(set bundle_pkginfo "#{@bundle_contents_dir}/PkgInfo")
(file bundle_pkginfo => @bundle_contents_dir is
(SH "echo -n 'APPL#{@creator_code}' > '#{(target name)}'"))
(task "bundle" => @bundle_executable_name "bundle_resources" bundle_infoplist bundle_pkginfo))
;; use this to create all the linking and assembly tasks to build a statically-linkable library
(macro library-tasks
(unless (and @arch (@arch length))
(set @arch (list (NSString stringWithShellCommand:"arch"))))
;; library architecture-specific executable
(set @library_executables (NSMutableArray array))
(@arch each:
(do (architecture)
(set library_executable "build/#{architecture}/#{@library}.a")
(@library_executables addObject:library_executable)
(file library_executable => (@c_objects objectForKey:architecture) (@m_objects objectForKey:architecture) is
(set command "libtool -static #{((@c_objects objectForKey:architecture) join)} #{((@m_objects objectForKey:architecture) join)} -o '#{(target name)}'")
(SH command))))
;; library fat archive
(set @library_executable_name "#{@library}.a")
(file @library_executable_name => @library_executables is
(set command "lipo -create #{(@library_executables join)} -output '#{@library_executable_name}'")
(SH command))
(task "library" => @library_executable_name))
;; use this to create all the linking and assembly tasks to build a dynamically-linkable library
(macro dylib-tasks
(unless (and @arch (@arch length))
(set @arch (list (NSString stringWithShellCommand:"arch"))))
(set libext (if (eq (uname) "Darwin") (then "dylib") (else "so")))
;; library architecture-specific executable
(set @library_executables (NSMutableArray array))
(@arch each:
(do (architecture)
(set library_executable "build/#{architecture}/#{@dylib}.#{libext}")
(@library_executables addObject:library_executable)
(if (eq (uname) "Darwin")
(then (set dylibflag "-dynamiclib"))
(else (set dylibflag "-shared")))
(if (eq (uname) "Darwin")
(then (set archflags "-arch #{architecture}"))
(else (set archflags "")))
(file library_executable => (@c_objects objectForKey:architecture) (@m_objects objectForKey:architecture) is
(set command "#{@cc} #{((@c_objects objectForKey:architecture) join)} #{((@m_objects objectForKey:architecture) join)} #{archflags} #{@cflags} #{@ldflags} #{dylibflag} -o '#{(target name)}'")
(SH command))))
;; fat dynamic library
(set @library_executable_name "#{@dylib}.#{libext}")
(file @library_executable_name => @library_executables is
(if (eq (uname) "Darwin")
(then (set command "lipo -create #{(@library_executables join)} -output '#{@library_executable_name}'"))
(else (set command "cp '#{(@library_executables objectAtIndex:0)}' '#{@library_executable_name}'")))
(SH command))
(task "dylib" => @library_executable_name))
;; @abstract A project consisting of an interrelated set of NukeTasks.
;; @discussion NukeProjects gather together a related set of NukeTask task descriptions
;; and allow them to be more easily referred to by name. There is typically
;; one NukeProject for a given run of nuke. A Nukefile is evaluated inside
;; an instance method of a NukeProject; so all instance variables in a Nukefile
;; belong to the NukeProject instance.
(class NukeProject is NSObject
(ivars)
;; Get the dictionary of tasks managed by a project.
(imethod (id) tasks is @tasks)
;; Get a task by its name.
(imethod (id) taskNamed:(id) taskName is
(set mytask (@tasks objectForKey:taskName))
(if (eq mytask nil)
(set mytask ((NukeTask alloc) initWithName:taskName))
(@tasks setObject:mytask forKey:taskName))
mytask)
;; Add a new task by to a project.
;; The new task is specified by its name.
;; The task action should be specified as a block.
(imethod (id) addTask:(id) taskName action:(id) action is
(set mytask (self taskNamed:taskName))
(mytask setAction:action)
mytask)
;; Add a dependency between named tasks.
(imethod (void) addDependency:(id) taskName on:(id) dependency is
(if (eq YES (dependency isKindOfClass:NSString))
(then
(((self taskNamed:taskName) dependencies) addObject: (self taskNamed:dependency)))
(else ;; if it's not a string, the dependency must be enumerable
(dependency each:
(do (element)
(self addDependency:taskName on:element))))))
;; Perform the tasks needed to complete a named target task.
(imethod (void) nuke:(id) targetName is
(set target (@tasks objectForKey:targetName))
(if target
(then (target update))
(else (puts "error, unknown target: #{targetName}"))))
;; Initialize a NukeProject.
(imethod (id) init is
(super init)
(set @tasks ((NSMutableDictionary alloc) init))
self)
;; Load and evaluate code from a named file. This is typically used to read a Nukefile.
(imethod (id) load: (id) filename is
(load filename)
self))
;;;;;;;;;;;;;;;;;;;;;;;;;
;; main program
;;;;;;;;;;;;;;;;;;;;;;;;;
;; there's lots more that we could do here, like
;; adding command line options to get task lists
;; or to control the verbosity.
(set argv ((NuApplication sharedApplication) arguments))
(set target (if (> (argv count) 0)
(then (argv objectAtIndex:0))
(else "default")))
;; We expect there to be a file named "Nukefile" in the current directory
;; or in one of its containing directories
(function climbToFile (filename)
(set path (((NSProcessInfo processInfo) environment) objectForKey:"PWD"))
(until (or (eq path "/")
((NSFileManager defaultManager) fileExistsAtPath:(+ path "/" filename)))
(set path (path stringByDeletingLastPathComponent)))
(if ((NSFileManager defaultManager) fileExistsAtPath:(+ path "/" filename))
(puts "nuke: running in #{path}")
((NSFileManager defaultManager) changeCurrentDirectoryPath:path)))
(try
(climbToFile "Nukefile")
(set project (((NukeProject alloc) init) load:"Nukefile"))
(catch (exception)
(puts "nuke error: #{(exception reason)}")
;; bridge and call a standard C function
(set exit (NuBridgedFunction functionWithName:"exit" signature:"vi"))
(exit -1)))
(if $verbose
(((project tasks) allKeys) each:
(do (key)
(set mytask ((project tasks) objectForKey:key))
(if (eq (mytask isFile) 0) (puts "#{key}")))))
(project nuke:target)