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',
54 'pcl-reference-assemblies',
64 def attach (self
, bockbuild
):
66 DarwinProfile
.attach (self
, bockbuild
)
68 # quick disk space check (http://stackoverflow.com/questions/787776/)
69 s
= os
.statvfs(bockbuild
.root
)
70 free_space
= (s
.f_bavail
* s
.f_frsize
) / (1024 * 1024 * 1024) # in GB
73 error('Low disk space (less than 10GB), aborting')
75 # check for XQuartz installation (needed for libgdiplus)
76 if not os
.path
.exists('/opt/X11/include/X11/Xlib.h'):
78 'XQuartz is required to be installed (download from http://xquartz.macosforge.org/) ')
80 self
.MONO_ROOT
= "/Library/Frameworks/Mono.framework"
81 self
.BUILD_NUMBER
= "0"
82 self
.MDK_GUID
= "964ebddd-1ffe-47e7-8128-5ce17ffffb05"
84 system_mono_dir
= '/Library/Frameworks/Mono.framework/Versions/Current'
85 self
.env
.set('system_mono', os
.path
.join(
86 system_mono_dir
, 'bin', 'mono'))
87 self
.env
.set('system_mcs', os
.path
.join(system_mono_dir
, 'bin', 'mcs'))
89 self
.env
.set('system_mono_version', backtick(
90 '%s --version' % self
.env
.system_mono
)[0])
92 # config overrides for some programs to be functional while staged
94 self
.env
.set('GDK_PIXBUF_MODULE_FILE',
95 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache')
96 self
.env
.set('GDK_PIXBUF_MODULEDIR',
97 '%{staged_prefix}/lib/gdk-pixbuf-2.0/2.10.0/loaders')
98 self
.env
.set('PANGO_SYSCONFDIR', '%{staged_prefix}/etc')
99 self
.env
.set('PANGO_LIBDIR', '%{staged_prefix}/lib')
100 # self.env.set ('MONO_PATH', '%{staged_prefix}/lib/mono/4.0')
101 self
.debug_info
= ['gtk', 'cairo', 'glib',
102 'pango', 'mono', 'llvm', 'libgdiplus']
103 self
.cache_host
= None
105 def setup_release(self
):
106 self
.mono_package
= self
.release_packages
['mono']
107 dest
= os
.path
.join(self
.bockbuild
.build_root
, self
.mono_package
.source_dir_name
)
108 self
.mono_package
.fetch(dest
)
111 verbose('Mono version: %s' % self
.mono_package
.version
)
112 self
.RELEASE_VERSION
= self
.mono_package
.version
113 self
.prefix
= os
.path
.join(
114 self
.MONO_ROOT
, "Versions", self
.RELEASE_VERSION
)
116 if os
.path
.exists(self
.prefix
):
117 error('Prefix %s exists, and may interfere with the staged build. Please remove and try again.' % self
.prefix
)
119 self
.calculate_updateid()
121 self
.mono_package
.custom_version_str
= self
.FULL_VERSION
122 trace(self
.package_info('MDK'))
124 self
.dont_optimize
= ['pixman']
126 for p
in self
.release_packages
.values():
127 if p
.name
in self
.dont_optimize
:
129 self
.gcc_flags
.extend(['-O2'])
131 # THIS IS THE MAIN METHOD FOR MAKING A PACKAGE
133 self
.fix_gtksharp_configs()
134 self
.verify_binaries()
136 working
= self
.setup_working_dir()
139 self
.apply_blacklist(working
, 'mdk_blacklist.sh')
140 self
.make_updateinfo(working
, self
.MDK_GUID
)
141 mdk_pkg
= self
.run_pkgbuild(working
, "MDK")
144 shutil
.rmtree(working
)
146 def calculate_updateid(self
):
147 # Create the updateid
149 git_bin
= self
.bockbuild
.git_bin
150 trace("cur path is %s and git is %s" % (pwd
, git_bin
))
151 blame_rev_str
= 'cd %s; %s blame configure.ac HEAD | grep AC_INIT | sed \'s/ .*//\' ' % (
152 self
.mono_package
.workspace
, git_bin
)
153 blame_rev
= backtick(blame_rev_str
)[0]
154 trace("Last commit to the version string %s" % (blame_rev
))
155 version_number_str
= 'cd %s; %s log %s..HEAD --oneline | wc -l | sed \'s/ //g\'' % (
156 self
.mono_package
.workspace
, git_bin
, blame_rev
)
157 self
.BUILD_NUMBER
= backtick(version_number_str
)[0]
158 trace("Calculating commit distance, %s" % (self
.BUILD_NUMBER
))
159 self
.FULL_VERSION
= self
.RELEASE_VERSION
+ "." + self
.BUILD_NUMBER
162 parts
= self
.RELEASE_VERSION
.split(".")
163 version_list
= (parts
+ ["0"] * (3 - len(parts
)))[:4]
164 for i
in range(1, 3):
165 version_list
[i
] = version_list
[i
].zfill(2)
166 self
.updateid
= "".join(version_list
)
167 self
.updateid
+= self
.BUILD_NUMBER
.replace(
168 ".", "").zfill(9 - len(self
.updateid
))
171 # creates and returns the path to a working directory containing:
172 # PKGROOT/ - this root will be bundled into the .pkg and extracted at /
173 # Info{_sdk}.plist - used by packagemaker to make the installer
174 # resources/ - other resources used by packagemaker for the installer
175 def setup_working_dir(self
):
176 def make_package_symlinks(root
):
177 os
.symlink(self
.prefix
, os
.path
.join(root
, "Versions", "Current"))
178 currentlink
= os
.path
.join(self
.MONO_ROOT
, "Versions", "Current")
181 ("include", "Headers"),
182 ("lib", "Libraries"),
184 (os
.path
.join("lib", "libmono-2.0.dylib"), "Mono")
186 for srcname
, destname
in links
:
187 src
= os
.path
.join(currentlink
, srcname
)
188 dest
= os
.path
.join(root
, destname
)
189 # If the symlink exists, we remove it so we can create a fresh
191 if os
.path
.exists(dest
):
193 os
.symlink(src
, dest
)
195 tmpdir
= tempfile
.mkdtemp()
196 monoroot
= os
.path
.join(tmpdir
, "PKGROOT", self
.MONO_ROOT
[1:])
197 versions
= os
.path
.join(monoroot
, "Versions")
198 os
.makedirs(versions
)
200 print "Setting up temporary package directory:", tmpdir
203 self
.packaging_dir
= os
.path
.join(self
.directory
, "packaging")
204 run_shell('rsync -aPq %s/* %s' % (self
.packaging_dir
, tmpdir
), False)
206 packages_list
= string
.join(
207 [pkg
.desc
for pkg
in self
.release_packages
.values()], "\\\n")
208 deps_list
= 'bockbuild (rev. %s)\\\n' % bockbuild
.bockbuild_rev
+ string
.join(
209 [pkg
.desc
for pkg
in self
.toolchain_packages
.values()], "\\\n")
212 '@@MONO_VERSION@@': self
.RELEASE_VERSION
,
213 '@@MONO_RELEASE@@': self
.BUILD_NUMBER
,
214 '@@MONO_VERSION_RELEASE@@': self
.RELEASE_VERSION
+ '_' + self
.BUILD_NUMBER
,
215 '@@MONO_CSDK_GUID@@': self
.MDK_GUID
,
216 '@@MONO_VERSION_RELEASE_INT@@': self
.updateid
,
217 '@@PACKAGES@@': packages_list
,
218 '@@DEP_PACKAGES@@': deps_list
220 for dirpath
, d
, files
in os
.walk(tmpdir
):
222 if not name
.startswith('.'):
223 replace_in_file(os
.path
.join(dirpath
, name
), parameter_map
)
225 make_package_symlinks(monoroot
)
227 # copy to package root
228 run_shell('rsync -aPq "%s"/* "%s/%s"' %
229 (bockbuild
.package_root
, versions
, self
.RELEASE_VERSION
), False)
233 def apply_blacklist(self
, working_dir
, blacklist_name
):
234 print "Applying blacklist script:", blacklist_name
235 blacklist
= os
.path
.join(self
.packaging_dir
, blacklist_name
)
236 root
= os
.path
.join(working_dir
, "PKGROOT", self
.prefix
[1:])
237 run_shell('%s "%s" > /dev/null' % (blacklist
, root
), print_cmd
=False)
239 def run_pkgbuild(self
, working_dir
, package_type
):
240 print 'Running pkgbuild & productbuild...',
241 info
= self
.package_info(package_type
)
242 output
= os
.path
.join(self
.directory
, info
["filename"])
243 identifier
= "com.xamarin.mono-" + info
["type"] + ".pkg"
244 resources_dir
= os
.path
.join(working_dir
, "resources")
245 distribution_xml
= os
.path
.join(resources_dir
, "distribution.xml")
247 old_cwd
= os
.getcwd()
248 os
.chdir(working_dir
)
249 pkgbuild
= "/usr/bin/pkgbuild"
250 pkgbuild_cmd
= ' '.join([pkgbuild
,
251 "--identifier " + identifier
,
252 "--root '%s/PKGROOT'" % working_dir
,
253 "--version '%s'" % self
.RELEASE_VERSION
,
254 "--install-location '/'",
255 "--scripts '%s'" % resources_dir
,
257 os
.path
.join(working_dir
, "mono.pkg")])
259 run_shell(pkgbuild_cmd
)
261 productbuild
= "/usr/bin/productbuild"
262 productbuild_cmd
= ' '.join([productbuild
,
263 "--resources %s" % resources_dir
,
264 "--distribution %s" % distribution_xml
,
265 "--package-path %s" % working_dir
,
269 run_shell(productbuild_cmd
)
271 assert_exists(output
)
276 def make_updateinfo(self
, working_dir
, guid
):
277 updateinfo
= os
.path
.join(
278 working_dir
, "PKGROOT", self
.prefix
[1:], "updateinfo")
279 with
open(updateinfo
, "w") as updateinfo
:
280 updateinfo
.write(guid
+ ' ' + self
.updateid
+ "\n")
281 version_file
= os
.path
.join(
282 working_dir
, "PKGROOT", self
.prefix
[1:], "VERSION")
283 with
open(version_file
, "w") as version_file
:
284 version_file
.write(self
.FULL_VERSION
+ "\n")
286 def package_info(self
, pkg_type
):
287 arch
= self
.bockbuild
.cmd_options
.arch
289 if arch
== "darwin-32":
291 elif arch
== "darwin-64":
293 elif arch
== "darwin-universal":
294 arch_str
= "universal"
296 error ("Unknown architecture")
298 if self
.bockbuild
.cmd_options
.release_build
:
299 info
= (pkg_type
, self
.FULL_VERSION
, arch_str
)
301 info
= (pkg_type
, '%s-%s' % (git_shortid(self
.bockbuild
,
302 self
.mono_package
.workspace
), self
.FULL_VERSION
), arch_str
)
304 filename
= "MonoFramework-%s-%s.macos10.xamarin.%s.pkg" % info
310 def fix_line(self
, line
, matcher
):
311 def insert_install_root(matches
):
313 captures
= matches
.groupdict()
314 return 'target="%s"' % os
.path
.join(root
, "lib", captures
["lib"])
317 pattern
= r
'target="(?P<lib>.+\.dylib)"'
318 result
= re
.sub(pattern
, insert_install_root
, line
)
323 def fix_dllmap(self
, config
, matcher
):
324 handle
, temp
= tempfile
.mkstemp()
325 with
open(config
) as c
:
326 with
open(temp
, "w") as output
:
328 output
.write(self
.fix_line(line
, matcher
))
329 os
.rename(temp
, config
)
330 os
.system('chmod a+r %s' % config
)
332 def fix_gtksharp_configs(self
):
333 print 'Fixing GTK# configuration files...',
344 gac
= os
.path
.join(bockbuild
.package_root
, "lib", "mono", "gac")
345 confs
= [glob
.glob(os
.path
.join(gac
, x
, "*", "*.dll.config")) for x
in libs
]
346 for c
in itertools
.chain(*confs
):
348 self
.fix_dllmap(c
, lambda line
: "dllmap" in line
)
352 result
= " ".join(backtick("otool -L " + f
))
353 regex
= os
.path
.join(self
.MONO_ROOT
, "Versions", r
"(\d+\.\d+\.\d+)")
355 match
= re
.search(regex
, result
)
358 token
= match
.group(1)
360 if self
.RELEASE_VERSION
not in token
:
361 raise Exception("%s references Mono %s\n%s" % (f
, token
, text
))
363 def verify_binaries(self
):
364 bindir
= os
.path
.join(bockbuild
.package_root
, "bin")
365 for path
, dirs
, files
in os
.walk(bindir
):
367 f
= os
.path
.join(path
, name
)
368 file_type
= backtick('file "%s"' % f
)
369 if "Mach-O executable" in "".join(file_type
):
373 envscript
= '''#!/bin/sh
377 export DYLD_FALLBACK_LIBRARY_PATH="$INSTALLDIR/lib:/lib:/usr/lib"
378 export ACLOCAL_PATH="$INSTALLDIR/share/aclocal"
379 export CONFIG_SITE="$INSTALLDIR/$PROFNAME-config.site"
380 export MONO_GAC_PREFIX="$INSTALLDIR"
381 export MONO_ADDINS_REGISTRY="$ROOT/addinreg"
382 export MONO_INSTALL_PREFIX="$INSTALLDIR"
384 export PS1="\[\e[1;3m\][$PROFNAME] \w @ "
386 ''' % (self
.profile_name
, self
.staged_prefix
, self
.root
)
388 path
= os
.path
.join(self
.root
, self
.profile_name
+ '.sh')
390 with
open(path
, 'w') as f
:
393 os
.chmod(path
, os
.stat(path
).st_mode | stat
.S_IEXEC
)
395 subprocess
.call(['bash', '-c', path
])