Reduce default LLVM/BTLS Visual Studio build output.
[mono-project.git] / packaging / MacSDK / profile.py
blobb652211a09f473c2ce2817e8e92cf419b7493caf
1 import itertools
2 import os
3 import re
4 import shutil
5 import string
6 import sys
7 import tempfile
8 import subprocess
9 import stat
11 from bockbuild.darwinprofile import DarwinProfile
12 from bockbuild.util.util import *
13 from glob import glob
15 class MonoReleaseProfile(DarwinProfile):
16 description = 'The Mono Framework for MacOS'
17 packages = [
18 'gettext',
19 'pkg-config',
21 # Base Libraries
22 'libpng',
23 'libjpeg',
24 'libtiff',
25 'libgif',
26 'libxml2',
27 'freetype',
28 'fontconfig',
29 'pixman',
30 'cairo',
31 'libffi',
32 'glib',
33 'pango',
34 'atk',
35 'intltool',
36 'gdk-pixbuf',
37 'gtk+',
38 'libglade',
39 'sqlite',
40 'expat',
41 'ige-mac-integration',
43 # Theme
44 'libcroco',
45 'librsvg',
46 'hicolor-icon-theme',
47 'gtk-engines',
48 'murrine',
49 'xamarin-gtk-theme',
50 'gtk-quartz-engine',
52 # Mono
53 'mono',
54 'msbuild',
55 'pcl-reference-assemblies',
56 'libgdiplus',
57 'xsp',
58 'gtk-sharp',
59 'ironlangs',
60 'fsharp',
61 'mono-basic',
62 'nuget'
65 def attach (self, bockbuild):
66 self.min_version = 9
67 DarwinProfile.attach (self, bockbuild)
69 # quick disk space check (http://stackoverflow.com/questions/787776/)
70 s = os.statvfs(bockbuild.root)
71 free_space = (s.f_bavail * s.f_frsize) / (1024 * 1024 * 1024) # in GB
73 if free_space < 10:
74 error('Low disk space (less than 10GB), aborting')
76 # check for XQuartz installation (needed for libgdiplus)
77 if not os.path.exists('/opt/X11/include/X11/Xlib.h'):
78 error(
79 'XQuartz is required to be installed (download from http://xquartz.macosforge.org/) ')
81 self.MONO_ROOT = "/Library/Frameworks/Mono.framework"
82 self.BUILD_NUMBER = "0"
83 self.MDK_GUID = "964ebddd-1ffe-47e7-8128-5ce17ffffb05"
85 system_mono_dir = '/Library/Frameworks/Mono.framework/Versions/Current'
86 self.env.set('system_mono', os.path.join(
87 system_mono_dir, 'bin', 'mono'))
88 self.env.set('system_mcs', os.path.join(system_mono_dir, 'bin', 'mcs'))
90 self.env.set('system_mono_version', backtick(
91 '%s --version' % self.env.system_mono)[0])
93 # config overrides for some programs to be functional while staged
95 self.env.set('GDK_PIXBUF_MODULE_FILE',
96 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache')
97 self.env.set('GDK_PIXBUF_MODULEDIR',
98 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders')
99 self.env.set('PANGO_SYSCONFDIR', '%{staged_prefix}/etc')
100 self.env.set('PANGO_LIBDIR', '%{staged_prefix}/lib')
101 # self.env.set ('MONO_PATH', '%{staged_prefix}/lib/mono/4.0')
102 self.debug_info = ['gtk+', 'cairo', 'glib',
103 'pango', 'mono', 'llvm', 'libgdiplus']
104 self.cache_host = None
106 def setup_release(self):
107 self.mono_package = self.release_packages['mono']
108 dest = os.path.join(self.bockbuild.build_root, self.mono_package.source_dir_name)
109 self.mono_package.fetch(dest)
112 verbose('Mono version: %s' % self.mono_package.version)
113 self.RELEASE_VERSION = self.mono_package.version
114 self.prefix = os.path.join(
115 self.MONO_ROOT, "Versions", self.RELEASE_VERSION)
117 if os.path.exists(self.prefix):
118 error('Prefix %s exists, and may interfere with the staged build. Please remove and try again.' % self.prefix)
120 self.calculate_updateid()
122 self.mono_package.custom_version_str = self.FULL_VERSION
123 trace(self.package_info('MDK'))
125 self.dont_optimize = ['pixman']
127 for p in self.release_packages.values():
128 if p.name in self.dont_optimize:
129 continue
130 self.gcc_flags.extend(['-O2'])
132 # THIS IS THE MAIN METHOD FOR MAKING A PACKAGE
133 def package(self):
134 self.fix_gtksharp_configs()
135 self.verify_binaries()
137 working = self.setup_working_dir()
139 # make the MDK
140 self.apply_blacklist(working, 'mdk_blacklist.sh')
141 self.make_updateinfo(working, self.MDK_GUID)
142 mdk_pkg = self.run_pkgbuild(working, "MDK")
143 title(mdk_pkg)
145 shutil.rmtree(working)
147 def calculate_updateid(self):
148 # Create the updateid
149 pwd = os.getcwd()
150 git_bin = self.bockbuild.git_bin
151 trace("cur path is %s and git is %s" % (pwd, git_bin))
152 blame_rev_str = 'cd %s; %s blame configure.ac HEAD | grep AC_INIT | sed \'s/ .*//\' ' % (
153 self.mono_package.workspace, git_bin)
154 blame_rev = backtick(blame_rev_str)[0]
155 trace("Last commit to the version string %s" % (blame_rev))
156 version_number_str = 'cd %s; %s log %s..HEAD --oneline | wc -l | sed \'s/ //g\'' % (
157 self.mono_package.workspace, git_bin, blame_rev)
158 self.BUILD_NUMBER = backtick(version_number_str)[0]
159 trace("Calculating commit distance, %s" % (self.BUILD_NUMBER))
160 self.FULL_VERSION = self.RELEASE_VERSION + "." + self.BUILD_NUMBER
161 os.chdir(pwd)
163 parts = self.RELEASE_VERSION.split(".")
164 version_list = (parts + ["0"] * (3 - len(parts)))[:4]
165 for i in range(1, 3):
166 version_list[i] = version_list[i].zfill(2)
167 self.updateid = "".join(version_list)
168 self.updateid += self.BUILD_NUMBER.replace(
169 ".", "").zfill(9 - len(self.updateid))
170 trace(self.updateid)
172 # creates and returns the path to a working directory containing:
173 # PKGROOT/ - this root will be bundled into the .pkg and extracted at /
174 # Info{_sdk}.plist - used by packagemaker to make the installer
175 # resources/ - other resources used by packagemaker for the installer
176 def setup_working_dir(self):
177 def make_package_symlinks(root):
178 os.symlink(self.prefix, os.path.join(root, "Versions", "Current"))
179 currentlink = os.path.join(self.MONO_ROOT, "Versions", "Current")
180 links = [
181 ("bin", "Commands"),
182 ("include", "Headers"),
183 ("lib", "Libraries"),
184 ("", "Home"),
185 (os.path.join("lib", "libmono-2.0.dylib"), "Mono")
187 for srcname, destname in links:
188 src = os.path.join(currentlink, srcname)
189 dest = os.path.join(root, destname)
190 # If the symlink exists, we remove it so we can create a fresh
191 # one
192 if os.path.exists(dest):
193 os.unlink(dest)
194 os.symlink(src, dest)
196 tmpdir = tempfile.mkdtemp()
197 monoroot = os.path.join(tmpdir, "PKGROOT", self.MONO_ROOT[1:])
198 versions = os.path.join(monoroot, "Versions")
199 os.makedirs(versions)
201 print "Setting up temporary package directory:", tmpdir
203 # setup metadata
204 self.packaging_dir = os.path.join(self.directory, "packaging")
205 run_shell('rsync -aPq %s/* %s' % (self.packaging_dir, tmpdir), False)
207 packages_list = string.join(
208 [pkg.desc for pkg in self.release_packages.values()], "\\\n")
209 deps_list = 'bockbuild (rev. %s)\\\n' % bockbuild.bockbuild_rev + string.join(
210 [pkg.desc for pkg in self.toolchain_packages.values()], "\\\n")
212 parameter_map = {
213 '@@MONO_VERSION@@': self.RELEASE_VERSION,
214 '@@MONO_RELEASE@@': self.BUILD_NUMBER,
215 '@@MONO_VERSION_RELEASE@@': self.RELEASE_VERSION + '_' + self.BUILD_NUMBER,
216 '@@MONO_CSDK_GUID@@': self.MDK_GUID,
217 '@@MONO_VERSION_RELEASE_INT@@': self.updateid,
218 '@@PACKAGES@@': packages_list,
219 '@@DEP_PACKAGES@@': deps_list
221 for dirpath, d, files in os.walk(tmpdir):
222 for name in files:
223 if not name.startswith('.'):
224 replace_in_file(os.path.join(dirpath, name), parameter_map)
226 make_package_symlinks(monoroot)
228 # copy to package root
229 run_shell('rsync -aPq "%s"/* "%s/%s"' %
230 (bockbuild.package_root, versions, self.RELEASE_VERSION), False)
232 return tmpdir
234 def apply_blacklist(self, working_dir, blacklist_name):
235 print "Applying blacklist script:", blacklist_name
236 blacklist = os.path.join(self.packaging_dir, blacklist_name)
237 root = os.path.join(working_dir, "PKGROOT", self.prefix[1:])
238 run_shell('%s "%s" > /dev/null' % (blacklist, root), print_cmd=False)
240 def run_pkgbuild(self, working_dir, package_type):
241 print 'Running pkgbuild & productbuild...',
242 info = self.package_info(package_type)
243 output = os.path.join(self.directory, info["filename"])
244 identifier = "com.xamarin.mono-" + info["type"] + ".pkg"
245 resources_dir = os.path.join(working_dir, "resources")
246 distribution_xml = os.path.join(resources_dir, "distribution.xml")
248 old_cwd = os.getcwd()
249 os.chdir(working_dir)
250 pkgbuild = "/usr/bin/pkgbuild"
251 pkgbuild_cmd = ' '.join([pkgbuild,
252 "--identifier " + identifier,
253 "--root '%s/PKGROOT'" % working_dir,
254 "--version '%s'" % self.RELEASE_VERSION,
255 "--install-location '/'",
256 "--scripts '%s'" % resources_dir,
257 "--quiet",
258 os.path.join(working_dir, "mono.pkg")])
260 run_shell(pkgbuild_cmd)
262 productbuild = "/usr/bin/productbuild"
263 productbuild_cmd = ' '.join([productbuild,
264 "--resources %s" % resources_dir,
265 "--distribution %s" % distribution_xml,
266 "--package-path %s" % working_dir,
267 "--quiet",
268 output])
270 run_shell(productbuild_cmd)
272 assert_exists(output)
273 os.chdir(old_cwd)
274 print output
275 return output
277 def make_updateinfo(self, working_dir, guid):
278 updateinfo = os.path.join(
279 working_dir, "PKGROOT", self.prefix[1:], "updateinfo")
280 with open(updateinfo, "w") as updateinfo:
281 updateinfo.write(guid + ' ' + self.updateid + "\n")
282 version_file = os.path.join(
283 working_dir, "PKGROOT", self.prefix[1:], "VERSION")
284 with open(version_file, "w") as version_file:
285 version_file.write(self.FULL_VERSION + "\n")
287 def package_info(self, pkg_type):
288 arch = self.bockbuild.cmd_options.arch
289 arch_str = None
290 if arch == "darwin-32":
291 arch_str = "x86"
292 elif arch == "darwin-64":
293 arch_str = "x64"
294 elif arch == "darwin-universal":
295 arch_str = "universal"
296 else:
297 error ("Unknown architecture")
299 if self.bockbuild.cmd_options.release_build:
300 info = (pkg_type, self.FULL_VERSION, arch_str)
301 else:
302 info = (pkg_type, '%s-%s' % (git_shortid(self.bockbuild,
303 self.mono_package.workspace), self.FULL_VERSION), arch_str)
305 filename = "MonoFramework-%s-%s.macos10.xamarin.%s.pkg" % info
306 return {
307 "type": pkg_type,
308 "filename": filename
311 def fix_line(self, line, matcher):
312 def insert_install_root(matches):
313 root = self.prefix
314 captures = matches.groupdict()
315 return 'target="%s"' % os.path.join(root, "lib", captures["lib"])
317 if matcher(line):
318 pattern = r'target="(?P<lib>.+\.dylib)"'
319 result = re.sub(pattern, insert_install_root, line)
320 return result
321 else:
322 return line
324 def fix_dllmap(self, config, matcher):
325 handle, temp = tempfile.mkstemp()
326 with open(config) as c:
327 with open(temp, "w") as output:
328 for line in c:
329 output.write(self.fix_line(line, matcher))
330 os.rename(temp, config)
331 os.system('chmod a+r %s' % config)
333 def fix_gtksharp_configs(self):
334 print 'Fixing GTK# configuration files...',
335 count = 0
336 libs = [
337 'atk-sharp',
338 'gdk-sharp',
339 'glade-sharp',
340 'glib-sharp',
341 'gtk-dotnet',
342 'gtk-sharp',
343 'pango-sharp'
345 gac = os.path.join(bockbuild.package_root, "lib", "mono", "gac")
346 confs = [glob.glob(os.path.join(gac, x, "*", "*.dll.config")) for x in libs]
347 for c in itertools.chain(*confs):
348 count = count + 1
349 self.fix_dllmap(c, lambda line: "dllmap" in line)
350 print count
352 def verify(self, f):
353 result = " ".join(backtick("otool -L " + f))
354 regex = os.path.join(self.MONO_ROOT, "Versions", r"(\d+\.\d+\.\d+)")
356 match = re.search(regex, result)
357 if match is None:
358 return
359 token = match.group(1)
360 trace(token)
361 if self.RELEASE_VERSION not in token:
362 raise Exception("%s references Mono %s\n%s" % (f, token, text))
364 def verify_binaries(self):
365 bindir = os.path.join(bockbuild.package_root, "bin")
366 for path, dirs, files in os.walk(bindir):
367 for name in files:
368 f = os.path.join(path, name)
369 file_type = backtick('file "%s"' % f)
370 if "Mach-O executable" in "".join(file_type):
371 self.verify(f)
373 def shell(self):
374 envscript = '''#!/bin/sh
375 PROFNAME="%s"
376 INSTALLDIR="%s"
377 ROOT="%s"
378 export DYLD_FALLBACK_LIBRARY_PATH="$INSTALLDIR/lib:/lib:/usr/lib"
379 export ACLOCAL_PATH="$INSTALLDIR/share/aclocal"
380 export CONFIG_SITE="$INSTALLDIR/$PROFNAME-config.site"
381 export MONO_GAC_PREFIX="$INSTALLDIR"
382 export MONO_ADDINS_REGISTRY="$ROOT/addinreg"
383 export MONO_INSTALL_PREFIX="$INSTALLDIR"
385 export PS1="\[\e[1;3m\][$PROFNAME] \w @ "
386 bash -i
387 ''' % (self.profile_name, self.staged_prefix, self.root)
389 path = os.path.join(self.root, self.profile_name + '.sh')
391 with open(path, 'w') as f:
392 f.write(envscript)
394 os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
396 subprocess.call(['bash', '-c', path])
398 MonoReleaseProfile()