11 from bockbuild.darwinprofile import DarwinProfile
12 from bockbuild.util.util import *
15 class MonoReleaseProfile(DarwinProfile):
16 description = 'The Mono Framework for MacOS'
41 'ige-mac-integration',
56 'pcl-reference-assemblies',
66 def attach (self, bockbuild):
68 DarwinProfile.attach (self, bockbuild)
70 # quick disk space check (http://stackoverflow.com/questions/787776/)
71 s = os.statvfs(bockbuild.root)
72 free_space = (s.f_bavail * s.f_frsize) / (1024 * 1024 * 1024) # in GB
75 error('Low disk space (less than 10GB), aborting')
77 # check for XQuartz installation (needed for libgdiplus)
78 if not os.path.exists('/opt/X11/include/X11/Xlib.h'):
80 'XQuartz is required to be installed (download from http://xquartz.macosforge.org/) ')
82 self.MONO_ROOT = "/Library/Frameworks/Mono.framework"
83 self.BUILD_NUMBER = "0"
84 self.MDK_GUID = "964ebddd-1ffe-47e7-8128-5ce17ffffb05"
86 system_mono_dir = '/Library/Frameworks/Mono.framework/Versions/Current'
87 self.env.set('system_mono', os.path.join(
88 system_mono_dir, 'bin', 'mono'))
89 self.env.set('system_mcs', os.path.join(system_mono_dir, 'bin', 'mcs'))
91 self.env.set('system_mono_version', backtick(
92 '%s --version' % self.env.system_mono)[0])
94 # config overrides for some programs to be functional while staged
96 self.env.set('GDK_PIXBUF_MODULE_FILE',
97 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache')
98 self.env.set('GDK_PIXBUF_MODULEDIR',
99 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders')
100 self.env.set('PANGO_SYSCONFDIR', '%{staged_prefix}/etc')
101 self.env.set('PANGO_LIBDIR', '%{staged_prefix}/lib')
102 # self.env.set ('MONO_PATH', '%{staged_prefix}/lib/mono/4.0')
103 self.debug_info = ['gtk+', 'cairo',
104 'pango', 'mono', 'llvm', 'libgdiplus']
105 self.cache_host = None
107 def setup_release(self):
108 self.mono_package = self.release_packages['mono']
109 dest = os.path.join(self.bockbuild.build_root, self.mono_package.source_dir_name)
110 self.mono_package.fetch(dest)
113 verbose('Mono version: %s' % self.mono_package.version)
114 self.RELEASE_VERSION = self.mono_package.version
115 self.prefix = os.path.join(
116 self.MONO_ROOT, "Versions", self.RELEASE_VERSION)
118 if os.path.exists(self.prefix):
119 error('Prefix %s exists, and may interfere with the staged build. Please remove and try again.' % self.prefix)
121 self.calculate_updateid()
123 self.mono_package.custom_version_str = self.FULL_VERSION
124 trace(self.package_info('MDK'))
126 self.dont_optimize = ['pixman']
128 for p in self.release_packages.values():
129 if p.name in self.dont_optimize:
131 self.gcc_flags.extend(['-O2'])
133 # THIS IS THE MAIN METHOD FOR MAKING A PACKAGE
135 self.fix_gtksharp_configs()
136 self.verify_binaries()
138 working = self.setup_working_dir()
139 uninstall_script = os.path.join(working, "uninstallMono.sh")
142 self.apply_blacklist(working, 'mdk_blacklist.sh')
143 self.make_updateinfo(working, self.MDK_GUID)
144 mdk_pkg = self.run_pkgbuild(working, "MDK")
146 # self.make_dmg(mdk_dmg, title, mdk_pkg, uninstall_script)
148 shutil.rmtree(working)
150 def calculate_updateid(self):
151 # Create the updateid
153 git_bin = self.bockbuild.git_bin
154 trace("cur path is %s and git is %s" % (pwd, git_bin))
155 blame_rev_str = 'cd %s; %s blame configure.ac HEAD | grep AC_INIT | sed \'s/ .*//\' ' % (
156 self.mono_package.workspace, git_bin)
157 blame_rev = backtick(blame_rev_str)[0]
158 trace("Last commit to the version string %s" % (blame_rev))
159 version_number_str = 'cd %s; %s log %s..HEAD --oneline | wc -l | sed \'s/ //g\'' % (
160 self.mono_package.workspace, git_bin, blame_rev)
161 self.BUILD_NUMBER = backtick(version_number_str)[0]
162 trace("Calculating commit distance, %s" % (self.BUILD_NUMBER))
163 self.FULL_VERSION = self.RELEASE_VERSION + "." + self.BUILD_NUMBER
166 parts = self.RELEASE_VERSION.split(".")
167 version_list = (parts + ["0"] * (3 - len(parts)))[:4]
168 for i in range(1, 3):
169 version_list[i] = version_list[i].zfill(2)
170 self.updateid = "".join(version_list)
171 self.updateid += self.BUILD_NUMBER.replace(
172 ".", "").zfill(9 - len(self.updateid))
175 # creates and returns the path to a working directory containing:
176 # PKGROOT/ - this root will be bundled into the .pkg and extracted at /
177 # uninstallMono.sh - copied onto the DMG
178 # Info{_sdk}.plist - used by packagemaker to make the installer
179 # resources/ - other resources used by packagemaker for the installer
180 def setup_working_dir(self):
181 def make_package_symlinks(root):
182 os.symlink(self.prefix, os.path.join(root, "Versions", "Current"))
183 currentlink = os.path.join(self.MONO_ROOT, "Versions", "Current")
186 ("include", "Headers"),
187 ("lib", "Libraries"),
189 (os.path.join("lib", "libmono-2.0.dylib"), "Mono")
191 for srcname, destname in links:
192 src = os.path.join(currentlink, srcname)
193 dest = os.path.join(root, destname)
194 # If the symlink exists, we remove it so we can create a fresh
196 if os.path.exists(dest):
198 os.symlink(src, dest)
200 tmpdir = tempfile.mkdtemp()
201 monoroot = os.path.join(tmpdir, "PKGROOT", self.MONO_ROOT[1:])
202 versions = os.path.join(monoroot, "Versions")
203 os.makedirs(versions)
205 print "Setting up temporary package directory:", tmpdir
208 self.packaging_dir = os.path.join(self.directory, "packaging")
209 run_shell('rsync -aPq %s/* %s' % (self.packaging_dir, tmpdir), False)
211 packages_list = string.join(
212 [pkg.desc for pkg in self.release_packages.values()], "\\\n")
213 deps_list = 'bockbuild (rev. %s)\\\n' % bockbuild.bockbuild_rev + string.join(
214 [pkg.desc for pkg in self.toolchain_packages.values()], "\\\n")
217 '@@MONO_VERSION@@': self.RELEASE_VERSION,
218 '@@MONO_RELEASE@@': self.BUILD_NUMBER,
219 '@@MONO_VERSION_RELEASE@@': self.RELEASE_VERSION + '_' + self.BUILD_NUMBER,
220 '@@MONO_CSDK_GUID@@': self.MDK_GUID,
221 '@@MONO_VERSION_RELEASE_INT@@': self.updateid,
222 '@@PACKAGES@@': packages_list,
223 '@@DEP_PACKAGES@@': deps_list
225 for dirpath, d, files in os.walk(tmpdir):
227 if not name.startswith('.'):
228 replace_in_file(os.path.join(dirpath, name), parameter_map)
230 make_package_symlinks(monoroot)
232 # copy to package root
233 run_shell('rsync -aPq "%s"/* "%s/%s"' %
234 (bockbuild.package_root, versions, self.RELEASE_VERSION), False)
238 def apply_blacklist(self, working_dir, blacklist_name):
239 print "Applying blacklist script:", blacklist_name
240 blacklist = os.path.join(self.packaging_dir, blacklist_name)
241 root = os.path.join(working_dir, "PKGROOT", self.prefix[1:])
242 run_shell('%s "%s" > /dev/null' % (blacklist, root), print_cmd=False)
244 def run_pkgbuild(self, working_dir, package_type):
245 print 'Running pkgbuild & productbuild...',
246 info = self.package_info(package_type)
247 output = os.path.join(self.directory, info["filename"])
248 identifier = "com.xamarin.mono-" + info["type"] + ".pkg"
249 resources_dir = os.path.join(working_dir, "resources")
250 distribution_xml = os.path.join(resources_dir, "distribution.xml")
252 old_cwd = os.getcwd()
253 os.chdir(working_dir)
254 pkgbuild = "/usr/bin/pkgbuild"
255 pkgbuild_cmd = ' '.join([pkgbuild,
256 "--identifier " + identifier,
257 "--root '%s/PKGROOT'" % working_dir,
258 "--version '%s'" % self.RELEASE_VERSION,
259 "--install-location '/'",
260 "--scripts '%s'" % resources_dir,
262 os.path.join(working_dir, "mono.pkg")])
264 run_shell(pkgbuild_cmd)
266 productbuild = "/usr/bin/productbuild"
267 productbuild_cmd = ' '.join([productbuild,
268 "--resources %s" % resources_dir,
269 "--distribution %s" % distribution_xml,
270 "--package-path %s" % working_dir,
274 run_shell(productbuild_cmd)
276 assert_exists(output)
281 def make_updateinfo(self, working_dir, guid):
282 updateinfo = os.path.join(
283 working_dir, "PKGROOT", self.prefix[1:], "updateinfo")
284 with open(updateinfo, "w") as updateinfo:
285 updateinfo.write(guid + ' ' + self.updateid + "\n")
286 version_file = os.path.join(
287 working_dir, "PKGROOT", self.prefix[1:], "VERSION")
288 with open(version_file, "w") as version_file:
289 version_file.write(self.FULL_VERSION + "\n")
291 def package_info(self, pkg_type):
292 arch = self.bockbuild.cmd_options.arch
294 if arch == "darwin-32":
296 elif arch == "darwin-64":
298 elif arch == "darwin-universal":
299 arch_str = "universal"
301 error ("Unknown architecture")
303 if self.bockbuild.cmd_options.release_build:
304 info = (pkg_type, self.FULL_VERSION, arch_str)
306 info = (pkg_type, '%s-%s' % (git_shortid(self.bockbuild,
307 self.mono_package.workspace), self.FULL_VERSION), arch_str)
309 filename = "MonoFramework-%s-%s.macos10.xamarin.%s.pkg" % info
315 def fix_line(self, line, matcher):
316 def insert_install_root(matches):
318 captures = matches.groupdict()
319 return 'target="%s"' % os.path.join(root, "lib", captures["lib"])
322 pattern = r'target="(?P<lib>.+\.dylib)"'
323 result = re.sub(pattern, insert_install_root, line)
328 def fix_dllmap(self, config, matcher):
329 handle, temp = tempfile.mkstemp()
330 with open(config) as c:
331 with open(temp, "w") as output:
333 output.write(self.fix_line(line, matcher))
334 os.rename(temp, config)
335 os.system('chmod a+r %s' % config)
337 def fix_gtksharp_configs(self):
338 print 'Fixing GTK# configuration files...',
349 gac = os.path.join(bockbuild.package_root, "lib", "mono", "gac")
350 confs = [glob.glob(os.path.join(gac, x, "*", "*.dll.config")) for x in libs]
351 for c in itertools.chain(*confs):
353 self.fix_dllmap(c, lambda line: "dllmap" in line)
357 result = " ".join(backtick("otool -L " + f))
358 regex = os.path.join(self.MONO_ROOT, "Versions", r"(\d+\.\d+\.\d+)")
360 match = re.search(regex, result)
363 token = match.group(1)
365 if self.RELEASE_VERSION not in token:
366 raise Exception("%s references Mono %s\n%s" % (f, token, text))
368 def verify_binaries(self):
369 bindir = os.path.join(bockbuild.package_root, "bin")
370 for path, dirs, files in os.walk(bindir):
372 f = os.path.join(path, name)
373 file_type = backtick('file "%s"' % f)
374 if "Mach-O executable" in "".join(file_type):
378 envscript = '''#!/bin/sh
382 export DYLD_FALLBACK_LIBRARY_PATH="$INSTALLDIR/lib:/lib:/usr/lib"
383 export ACLOCAL_PATH="$INSTALLDIR/share/aclocal"
384 export CONFIG_SITE="$INSTALLDIR/$PROFNAME-config.site"
385 export MONO_GAC_PREFIX="$INSTALLDIR"
386 export MONO_ADDINS_REGISTRY="$ROOT/addinreg"
387 export MONO_INSTALL_PREFIX="$INSTALLDIR"
389 export PS1="\[\e[1;3m\][$PROFNAME] \w @ "
391 ''' % (self.profile_name, self.staged_prefix, self.root)
393 path = os.path.join(self.root, self.profile_name + '.sh')
395 with open(path, 'w') as f:
398 os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
400 subprocess.call(['bash', '-c', path])