1require 'rake/loaders/makefile'
2
3@cc = 'cc'
4@linker = 'cc'
5@makedepend = 'makedepend'
6@frameworkPaths = %w(~/Library/Frameworks /Library/Frameworks /System/Library/Frameworks)
7
8@commonflags = ''
9@cflags = ''
10@cxxflags = ''
11@objcflags = ''
12
13@includes = ''
14@defines = ''
15@ldflags = ''
16@frameworkFlags = ''
17@libs = ''
18@systemFrameworks = []
19@frameworks = []
20@cleanfiles = []
21
22begin
23	unless NOT_UNIVERSAL
24		ENV['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
25		@commonflags += ' -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk '
26		@ldflags += ' -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk '
27	end
28rescue NameError
29	NOT_UNIVERSAL = false
30	retry
31end
32
33begin
34	if BUILDDIR
35		@includes += " -I#{BUILDDIR} "
36	end
37rescue NameError
38	BUILDDIR = ''
39end
40
41###########
42# Cleanup #
43###########
44
45task(:clean) do |task|
46	rm_rf(BUILDDIR)
47	rm_rf(@cleanfiles)
48	rm_rf(BUNDLEDIR)
49end
50
51################
52# Installation #
53################
54
55def installRule(dir, file)
56	target = File::join(dir, File::basename(file))
57	file(target => [dir, file]) do |t|
58		cp(file, target)
59	end
60end
61
62def installTask(taskName, dir, files)
63	if files.is_a?(String)
64		files = [files]
65	end
66
67	array = []
68	for f in files do
69		array << File::join(dir, File::basename(f))
70		installRule(dir, f)
71	end
72	task(taskName => array)
73end
74
75def installTaskRecursive(taskName, dest, src)
76	target = File::join(dest, File::basename(src))
77	file(target => [dest, src]) do |t|
78		rm_rf(t.name)
79		cp_r(t.prerequisites[1], t.prerequisites[0])
80	end
81	task(taskName => target)
82end
83
84def bundleDir(dir)
85	file(dir) do |t|
86		sh("/Developer/Tools/SetFile -a B #{dir}")
87	end
88end
89
90def setCurrentVersion(taskName)
91	task(taskName => "#{VERSIONSDIR}/#{FRAMEWORKVERSION}") do |t|
92		for file in Dir::glob("#{t.prerequisites[0]}/*") do
93			filename = File::basename(file)
94			ln_s("Contents/Versions/#{FRAMEWORKVERSION}/#{filename}", "#{BUNDLEDIR}/#{filename}", :force => true)
95		end
96	end
97end
98
99begin
100	if FRAMEWORK
101		directory(BUNDLEDIR = "#{BUILDDIR}#{NAME}.framework")
102		directory(CONTENTSDIR = "#{BUNDLEDIR}/Contents")
103		directory(VERSIONSDIR = "#{CONTENTSDIR}/Versions")
104		directory(VERSIONDIR = "#{VERSIONSDIR}/#{FRAMEWORKVERSION}")
105		directory(RESOURCEDIR = "#{VERSIONDIR}/Resources")
106		directory(HEADERDIR = "#{VERSIONDIR}/Headers")
107
108		@ldflags += " -dynamiclib -install_name @executable_path/../Frameworks/#{VERSIONDIR}/#{NAME}"
109	else
110		directory(BUNDLEDIR = "#{BUILDDIR}#{NAME}.app")
111		directory(CONTENTSDIR = "#{BUNDLEDIR}/Contents")
112		directory(RESOURCEDIR = "#{CONTENTSDIR}/Resources")
113		directory(BINDIR = "#{CONTENTSDIR}/MacOS")
114		directory(FRAMEWORKDIR = "#{CONTENTSDIR}/Frameworks")
115	end
116rescue NameError => e
117	if e.name == :FRAMEWORK
118		FRAMEWORK = false
119		retry
120	else
121		raise e
122	end
123end
124
125bundleDir(BUNDLEDIR)
126task(:bundle => BUNDLEDIR)
127
128##############
129# Frameworks #
130##############
131
132def installFrameworks(task)
133	for name in [@frameworks, @systemFrameworks].flatten
134		framework = nil
135		for path in @frameworkPaths
136			try = "#{File::expand_path(path)}/#{name}.framework"
137			if File::directory?(try)
138				framework = try
139				break
140			end
141		end
142
143		if not framework
144			puts "Framework #{name} missing!"
145			exit(1)
146		end
147
148		@frameworkFlags += " -framework #{name} "
149		@includes += " -I#{framework}/Headers "
150
151		if @frameworks.include?(name)
152			installTaskRecursive(task, FRAMEWORKDIR, framework)
153			bundleDir("#{FRAMEWORKDIR}/#{name}.framework")
154		end
155	end
156
157	for dir in @frameworkPaths
158		@cflags += " -F#{File::expand_path(dir)} "
159		@ldflags += " -F#{File::expand_path(dir)} "
160	end
161end
162
163###############
164# Compilation #
165###############
166
167def cTask(object, source)
168	file(object => source) do |task|
169		sh("#{@cc} #{@commonflags} #{@cflags} #{@includes} #{@defines} -o \"#{task.name}\" -c #{task.prerequisites[0]}")
170	end
171end
172
173def objcTask(object, source)
174	file(object => source) do |task|
175		sh("#{@cc} #{@commonflags} #{@objcflags} #{@includes} #{@defines} -o \"#{task.name}\" -c #{task.prerequisites[0]}")
176	end
177end
178
179def objcxxTask(object, source)
180	file(object => source) do |task|
181		sh("#{@cc} #{@commonflags} #{@cxxflags} #{@objcflags} #{@includes} #{@defines} -o \"#{task.name}\" -c #{task.prerequisites[0]}")
182	end
183end
184
185def cxxTask(object, source)
186	file(object => source) do |task|
187		sh("#{@cc} #{@commonflags} #{@cxxflags} #{@includes} #{@defines} -o \"#{task.name}\" -c #{task.prerequisites[0]}")
188	end
189end
190
191def buildBinary(task, path, file, sources)
192	objects = []
193	target = "#{path}/#{file}"
194	for source in sources
195		object = "#{BUILDDIR}#{File::dirname(source)}/#{File::basename(source, '.*')}.o"
196		if ['.m'].include?(File::extname(source))
197			objcTask(object, source)
198		elsif ['.M', '.mm'].include?(File::extname(source))
199			objcxxTask(object, source)
200		elsif ['.cxx', '.cc', '.cpp', '.C'].include?(File::extname(source))
201			cxxTask(object, source)
202		else
203			cTask(object, source)
204		end
205		objects.push(object)
206		@cleanfiles.push(object)
207
208		deps = [source]
209		if BUILDDIR != ''
210			dir = "#{BUILDDIR}#{File::dirname(source)}"
211			directory(dir)
212			deps.push(dir)
213		end
214		depfile = "#{BUILDDIR}#{File::dirname(source)}/.#{File::basename(source, '.*')}.dep.mf"
215		file(depfile => deps) do |task|
216			prefix = File::join(File::dirname(task.prerequisites[0]), '')
217			sh("#{@makedepend} -p#{BUILDDIR}#{prefix} -f- -- #{@includes} #{@defines} -- #{task.prerequisites[0]} > #{task.name} 2> /dev/null")
218		end
219		import depfile
220		@cleanfiles.push(depfile)
221	end
222
223	file(target => [path, *objects]) do |task|
224		sh("#{@linker} #{@frameworkFlags} #{@ldflags} #{@libs} -o \"#{task.name}\" #{task.prerequisites[1..-1].join(' ')}")
225	end
226
227	task(task => target)
228end
229