mx_jvmci.py revision 9826:6a85f279e4c7
1#
2# ----------------------------------------------------------------------------------------------------
3#
4# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
5# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6#
7# This code is free software; you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 2 only, as
9# published by the Free Software Foundation.
10#
11# This code is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14# version 2 for more details (a copy is included in the LICENSE file that
15# accompanied this code).
16#
17# You should have received a copy of the GNU General Public License version
18# 2 along with this work; if not, write to the Free Software Foundation,
19# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20#
21# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22# or visit www.oracle.com if you need additional information or have any
23# questions.
24#
25# ----------------------------------------------------------------------------------------------------
26
27import os, shutil, zipfile, re, time, sys, datetime, platform
28from os.path import join, exists, dirname, isdir
29from argparse import ArgumentParser, REMAINDER
30import StringIO
31import xml.dom.minidom
32import subprocess
33
34import mx
35import mx_gate
36import mx_unittest
37
38from mx_gate import Task
39from mx_unittest import unittest
40
41_suite = mx.suite('jvmci')
42
43"""
44Top level directory of the JDK source workspace.
45"""
46_jdkSourceRoot = dirname(_suite.dir)
47
48_JVMCI_JDK_TAG = 'jvmci'
49
50_minVersion = mx.VersionSpec('1.9')
51
52# max version (first _unsupported_ version)
53_untilVersion = None
54
55_jvmciModes = {
56    'hosted' : ['-XX:+UnlockExperimentalVMOptions', '-XX:+EnableJVMCI'],
57    'jit' : ['-XX:+UnlockExperimentalVMOptions', '-XX:+EnableJVMCI', '-XX:+UseJVMCICompiler'],
58    'disabled' : []
59}
60
61# TODO: can optimized be built without overriding release build?
62_jdkDebugLevels = ['release', 'fastdebug', 'slowdebug']
63
64# TODO: add client once/if it can be built on 64-bit platforms
65_jdkJvmVariants = ['server']
66
67"""
68Translation table from mx_jvmci:8 --vmbuild values to mx_jvmci:9 --jdk-debug-level values.
69"""
70_legacyVmbuilds = {
71    'product' : 'release',
72    'debug' : 'slowdebug'
73}
74
75"""
76Translates a mx_jvmci:8 --vmbuild value to a mx_jvmci:9 --jdk-debug-level value.
77"""
78def _translateLegacyDebugLevel(debugLevel):
79    return _legacyVmbuilds.get(debugLevel, debugLevel)
80
81"""
82Translation table from mx_jvmci:8 --vm values to mx_jvmci:9 (--jdk-jvm-variant, --jvmci-mode) tuples.
83"""
84_legacyVms = {
85    'jvmci' : ('server', 'jit')
86}
87
88"""
89A VM configuration composed of a JDK debug level, JVM variant and a JVMCI mode.
90This is also a context manager that can be used with the 'with' statement to set/change
91a VM configuration within a dynamic scope. For example:
92
93    with ConfiguredJDK(debugLevel='fastdebug'):
94        dacapo(['pmd'])
95"""
96class VM:
97    def __init__(self, jvmVariant=None, debugLevel=None, jvmciMode=None):
98        self.update(jvmVariant, debugLevel, jvmciMode)
99
100    def update(self, jvmVariant=None, debugLevel=None, jvmciMode=None):
101        if jvmVariant in _legacyVms:
102            # Backwards compatibility for mx_jvmci:8 API
103            jvmVariant, newJvmciMode = _legacyVms[jvmVariant]
104            if jvmciMode is not None and jvmciMode != newJvmciMode:
105                mx.abort('JVM variant "' + jvmVariant + '" implies JVMCI mode "' + newJvmciMode +
106                         '" which conflicts with explicitly specified JVMCI mode of "' + jvmciMode + '"')
107            jvmciMode = newJvmciMode
108        debugLevel = _translateLegacyDebugLevel(debugLevel)
109        assert jvmVariant is None or jvmVariant in _jdkJvmVariants, jvmVariant
110        assert debugLevel is None or debugLevel in _jdkDebugLevels, debugLevel
111        assert jvmciMode is None or jvmciMode in _jvmciModes, jvmciMode
112        self.jvmVariant = jvmVariant or _vm.jvmVariant
113        self.debugLevel = debugLevel or _vm.debugLevel
114        self.jvmciMode = jvmciMode or _vm.jvmciMode
115
116    def __enter__(self):
117        global _vm
118        self.previousVm = _vm
119        _vm = self
120
121    def __exit__(self, exc_type, exc_value, traceback):
122        global _vm
123        _vm = self.previousVm
124
125_vm = VM(jvmVariant=_jdkJvmVariants[0], debugLevel=_jdkDebugLevels[0], jvmciMode='hosted')
126
127def get_vm():
128    """
129    Gets the configured VM.
130    """
131    return _vm
132
133def relativeVmLibDirInJdk():
134    mxos = mx.get_os()
135    if mxos == 'darwin':
136        return join('lib')
137    if mxos == 'windows' or mxos == 'cygwin':
138        return join('bin')
139    return join('lib', mx.get_arch())
140
141def isJVMCIEnabled(vm):
142    assert vm in _jdkJvmVariants
143    return True
144
145class JvmciJDKDeployedDist(object):
146    def __init__(self, name, compilers=False):
147        self._name = name
148        self._compilers = compilers
149
150    def dist(self):
151        return mx.distribution(self._name)
152
153    def deploy(self, jdkDir):
154        mx.nyi('deploy', self)
155
156class ExtJDKDeployedDist(JvmciJDKDeployedDist):
157    def __init__(self, name):
158        JvmciJDKDeployedDist.__init__(self, name)
159
160
161"""
162The monolithic JVMCI distribution is deployed through use of -Xbootclasspath/p
163so that it's not necessary to run JDK make after editing JVMCI sources.
164The latter causes all JDK Java sources to be rebuilt since JVMCI is
165(currently) in java.base.
166"""
167_monolithicJvmci = JvmciJDKDeployedDist('JVMCI')
168
169"""
170List of distributions that are deployed on the boot class path.
171Note: In jvmci-8, they were deployed directly into the JDK directory.
172"""
173jdkDeployedDists = [_monolithicJvmci]
174
175def _makehelp():
176    return subprocess.check_output([mx.gmake_cmd(), 'help'], cwd=_jdkSourceRoot)
177
178def _runmake(args):
179    """run the JDK make process
180
181To build hotspot and import it into the JDK: "mx make hotspot import-hotspot"
182{0}"""
183
184    jdkBuildDir = _get_jdk_build_dir()
185    if not exists(jdkBuildDir):
186        # JDK9 must be bootstrapped with a JDK8
187        compliance = mx.JavaCompliance('8')
188        jdk8 = mx.get_jdk(compliance.exactMatch, versionDescription=compliance.value)
189        cmd = ['sh', 'configure', '--with-debug-level=' + _vm.debugLevel, '--disable-debug-symbols', '--disable-precompiled-headers',
190               '--with-jvm-variants=' + _vm.jvmVariant, '--disable-warnings-as-errors', '--with-boot-jdk=' + jdk8.home]
191        mx.run(cmd, cwd=_jdkSourceRoot)
192    cmd = [mx.gmake_cmd(), 'CONF=' + _vm.debugLevel]
193    if mx.get_opts().verbose:
194        cmd.append('LOG=debug')
195    cmd.extend(args)
196    if mx.get_opts().use_jdk_image and 'images' not in args:
197        cmd.append('images')
198
199    if not mx.get_opts().verbose:
200        mx.log('--------------- make execution ----------------------')
201        mx.log('Working directory: ' + _jdkSourceRoot)
202        mx.log('Command line: ' + ' '.join(cmd))
203        mx.log('-----------------------------------------------------')
204
205    mx.run(cmd, cwd=_jdkSourceRoot)
206
207    if 'images' in cmd:
208        _create_jdk_bundle(jdkBuildDir)
209
210def _get_jdk_bundle_arches():
211    """
212    Gets a list of names that will be the part of a JDK bundle's file name denoting the architecture.
213    The first element in the list is the canonical name. Symlinks should be created for the
214    remaining names.
215    """
216    cpu = mx.get_arch()
217    if cpu == 'amd64':
218        return ['x64', 'x86_64', 'amd64']
219    elif cpu == 'sparcv9':
220        return ['sparcv9']
221    mx.abort('Unsupported JDK bundle arch: ' + cpu)
222
223def _create_jdk_bundle(jdkBuildDir):
224    """
225    Creates a tar.gz JDK archive, an accompanying tar.gz.sha1 file with its
226    SHA1 signature plus symlinks to the archive for non-canonical architecture names.
227    """
228    jdkImageDir = join(jdkBuildDir, 'images', 'jdk')
229
230    arches = _get_jdk_bundle_arches()
231    jdkTgzPath = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arches[0]))
232    with mx.Archiver(jdkTgzPath, kind='tgz') as arc:
233        mx.log('Creating ' + jdkTgzPath)
234        for root, _, filenames in os.walk(jdkImageDir):
235            for name in filenames:
236                f = join(root, name)
237                arcname = 'jdk1.9.0/' + os.path.relpath(f, jdkImageDir)
238                arc.zf.add(name=f, arcname=arcname, recursive=False)
239        # The OpenJDK build creates an empty cacerts file so grab one from
240        # the default JDK which is assumed to be an OracleJDK
241        cacerts = join(mx.get_jdk(tag='default').home, 'jre', 'lib', 'security', 'cacerts')
242        arc.zf.add(name=cacerts, arcname='jdk1.9.0/lib/security/cacerts')
243
244    with open(jdkTgzPath + '.sha1', 'w') as fp:
245        mx.log('Creating ' + jdkTgzPath + '.sha1')
246        fp.write(mx.sha1OfFile(jdkTgzPath))
247
248    def _create_link(source, link_name):
249        if exists(link_name):
250            os.remove(link_name)
251        mx.log('Creating ' + link_name + ' -> ' + source)
252        os.symlink(source, link_name)
253
254    for arch in arches[1:]:
255        link_name = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arch))
256        jdkTgzName = os.path.basename(jdkTgzPath)
257        _create_link(jdkTgzName, link_name)
258        _create_link(jdkTgzName + '.sha1', link_name + '.sha1')
259
260def _runmultimake(args):
261    """run the JDK make process for one or more configurations"""
262
263    jvmVariantsDefault = ','.join(_jdkJvmVariants)
264    debugLevelsDefault = ','.join(_jdkDebugLevels)
265
266    parser = ArgumentParser(prog='mx multimake')
267    parser.add_argument('--jdk-jvm-variants', '--vms', help='a comma separated list of VMs to build (default: ' + jvmVariantsDefault + ')', metavar='<args>', default=jvmVariantsDefault)
268    parser.add_argument('--jdk-debug-levels', '--builds', help='a comma separated list of JDK debug levels (default: ' + debugLevelsDefault + ')', metavar='<args>', default=debugLevelsDefault)
269    parser.add_argument('-n', '--no-check', action='store_true', help='omit running "java -version" after each build')
270    select = parser.add_mutually_exclusive_group()
271    select.add_argument('-c', '--console', action='store_true', help='send build output to console instead of log files')
272    select.add_argument('-d', '--output-dir', help='directory for log files instead of current working directory', default=os.getcwd(), metavar='<dir>')
273
274    args = parser.parse_args(args)
275    jvmVariants = args.jdk_jvm_variants.split(',')
276    debugLevels = [_translateLegacyDebugLevel(dl) for dl in args.jdk_debug_levels.split(',')]
277
278    allStart = time.time()
279    for jvmVariant in jvmVariants:
280        for debugLevel in debugLevels:
281            if not args.console:
282                logFile = join(mx.ensure_dir_exists(args.output_dir), jvmVariant + '-' + debugLevel + '.log')
283                log = open(logFile, 'wb')
284                start = time.time()
285                mx.log('BEGIN: ' + jvmVariant + '-' + debugLevel + '\t(see: ' + logFile + ')')
286                verbose = ['-v'] if mx.get_opts().verbose else []
287                # Run as subprocess so that output can be directed to a file
288                cmd = [sys.executable, '-u', mx.__file__] + verbose + ['--jdk-jvm-variant=' + jvmVariant, '--jdk-debug-level=' + debugLevel, 'make']
289                mx.logv("executing command: " + str(cmd))
290                subprocess.check_call(cmd, cwd=_suite.dir, stdout=log, stderr=subprocess.STDOUT)
291                duration = datetime.timedelta(seconds=time.time() - start)
292                mx.log('END:   ' + jvmVariant + '-' + debugLevel + '\t[' + str(duration) + ']')
293            else:
294                with VM(jvmVariant=jvmVariant, debugLevel=debugLevel):
295                    _runmake([])
296            if not args.no_check:
297                with VM(jvmciMode='jit'):
298                    run_vm(['-XX:-BootstrapJVMCI', '-version'])
299    allDuration = datetime.timedelta(seconds=time.time() - allStart)
300    mx.log('TOTAL TIME:   ' + '[' + str(allDuration) + ']')
301
302class HotSpotProject(mx.NativeProject):
303    """
304    Defines a NativeProject representing the HotSpot binaries built via make.
305    """
306    def __init__(self, suite, name, deps, workingSets, **args):
307        assert name == 'hotspot'
308        mx.NativeProject.__init__(self, suite, name, "", [], deps, workingSets, None, None, join(suite.mxDir, name))
309
310    def eclipse_config_up_to_date(self, configZip):
311        # Assume that any change to this module might imply changes to the generated IDE files
312        if configZip.isOlderThan(__file__):
313            return False
314        for _, source in self._get_eclipse_settings_sources().iteritems():
315            if configZip.isOlderThan(source):
316                return False
317        return True
318
319    def _get_eclipse_settings_sources(self):
320        """
321        Gets a dictionary from the name of an Eclipse settings file to
322        the file providing its generated content.
323        """
324        if not hasattr(self, '_eclipse_settings'):
325            esdict = {}
326            templateSettingsDir = join(self.dir, 'templates', 'eclipse', 'settings')
327            if exists(templateSettingsDir):
328                for name in os.listdir(templateSettingsDir):
329                    source = join(templateSettingsDir, name)
330                    esdict[name] = source
331            self._eclipse_settings = esdict
332        return self._eclipse_settings
333
334    def _eclipseinit(self, files=None, libFiles=None):
335        """
336        Generates an Eclipse project for each HotSpot build configuration.
337        """
338
339        roots = [
340            'ASSEMBLY_EXCEPTION',
341            'LICENSE',
342            'README',
343            'THIRD_PARTY_README',
344            'agent',
345            'make',
346            'src',
347            'test'
348        ]
349
350        for jvmVariant in _jdkJvmVariants:
351            for debugLevel in _jdkDebugLevels:
352                name = jvmVariant + '-' + debugLevel
353                eclProjectDir = join(self.dir, 'eclipse', name)
354                mx.ensure_dir_exists(eclProjectDir)
355
356                out = mx.XMLDoc()
357                out.open('projectDescription')
358                out.element('name', data='hotspot:' + name)
359                out.element('comment', data='')
360                out.element('projects', data='')
361                out.open('buildSpec')
362                out.open('buildCommand')
363                out.element('name', data='org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder')
364                out.element('triggers', data='full,incremental')
365                out.element('arguments', data='')
366                out.close('buildCommand')
367
368                out.close('buildSpec')
369                out.open('natures')
370                out.element('nature', data='org.eclipse.cdt.core.cnature')
371                out.element('nature', data='org.eclipse.cdt.core.ccnature')
372                out.element('nature', data='org.eclipse.cdt.managedbuilder.core.managedBuildNature')
373                out.element('nature', data='org.eclipse.cdt.managedbuilder.core.ScannerConfigNature')
374                out.close('natures')
375
376                if roots:
377                    out.open('linkedResources')
378                    for r in roots:
379                        f = join(_suite.dir, r)
380                        out.open('link')
381                        out.element('name', data=r)
382                        out.element('type', data='2' if isdir(f) else '1')
383                        out.element('locationURI', data=mx.get_eclipse_project_rel_locationURI(f, eclProjectDir))
384                        out.close('link')
385
386                    out.open('link')
387                    out.element('name', data='generated')
388                    out.element('type', data='2')
389                    generated = join(_get_hotspot_build_dir(jvmVariant, debugLevel), 'generated')
390                    out.element('locationURI', data=mx.get_eclipse_project_rel_locationURI(generated, eclProjectDir))
391                    out.close('link')
392
393                    out.close('linkedResources')
394                out.close('projectDescription')
395                projectFile = join(eclProjectDir, '.project')
396                mx.update_file(projectFile, out.xml(indent='\t', newl='\n'))
397                if files:
398                    files.append(projectFile)
399
400                cprojectTemplate = join(self.dir, 'templates', 'eclipse', 'cproject')
401                cprojectFile = join(eclProjectDir, '.cproject')
402                with open(cprojectTemplate) as f:
403                    content = f.read()
404                mx.update_file(cprojectFile, content)
405                if files:
406                    files.append(cprojectFile)
407
408                settingsDir = join(eclProjectDir, ".settings")
409                mx.ensure_dir_exists(settingsDir)
410                for name, source in self._get_eclipse_settings_sources().iteritems():
411                    out = StringIO.StringIO()
412                    print >> out, '# GENERATED -- DO NOT EDIT'
413                    print >> out, '# Source:', source
414                    with open(source) as f:
415                        print >> out, f.read()
416                    content = out.getvalue()
417                    mx.update_file(join(settingsDir, name), content)
418                    if files:
419                        files.append(join(settingsDir, name))
420
421    def getBuildTask(self, args):
422        return JDKBuildTask(self, args, _vm.debugLevel, _vm.jvmVariant)
423
424
425class JDKBuildTask(mx.NativeBuildTask):
426    def __init__(self, project, args, debugLevel, jvmVariant):
427        mx.NativeBuildTask.__init__(self, args, project)
428        self.jvmVariant = jvmVariant
429        self.debugLevel = debugLevel
430
431    def __str__(self):
432        return 'Building JDK[{}, {}]'.format(self.debugLevel, self.jvmVariant)
433
434    def build(self):
435        if mx.get_opts().use_jdk_image:
436            _runmake(['images'])
437        else:
438            _runmake([])
439        self._newestOutput = None
440
441    def clean(self, forBuild=False):
442        if forBuild:  # Let make handle incremental builds
443            return
444        if exists(_get_jdk_build_dir(self.debugLevel)):
445            _runmake(['clean'])
446        self._newestOutput = None
447
448# Backwards compatibility for mx_jvmci:8 API
449def buildvms(args):
450    _runmultimake(args)
451
452def run_vm(args, vm=None, nonZeroIsFatal=True, out=None, err=None, cwd=None, timeout=None, debugLevel=None, vmbuild=None):
453    """run a Java program by executing the java executable in a JVMCI JDK"""
454    jdkTag = mx.get_jdk_option().tag
455    if jdkTag and jdkTag != _JVMCI_JDK_TAG:
456        mx.abort('The "--jdk" option must have the tag "' + _JVMCI_JDK_TAG + '" when running a command requiring a JVMCI VM')
457    jdk = get_jvmci_jdk(debugLevel=debugLevel or _translateLegacyDebugLevel(vmbuild))
458    return jdk.run_java(args, nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd, timeout=timeout)
459
460def _unittest_vm_launcher(vmArgs, mainClass, mainClassArgs):
461    run_vm(vmArgs + [mainClass] + mainClassArgs)
462
463mx_unittest.set_vm_launcher('JVMCI VM launcher', _unittest_vm_launcher)
464
465def _jvmci_gate_runner(args, tasks):
466    # Build release server VM now so we can run the unit tests
467    with Task('BuildHotSpotJVMCIHosted: release', tasks) as t:
468        if t: _runmultimake(['--jdk-jvm-variants', 'server', '--jdk-debug-levels', 'release'])
469
470    # Run unit tests in hosted mode
471    with VM(jvmVariant='server', debugLevel='release', jvmciMode='hosted'):
472        with Task('JVMCI UnitTests: hosted-release', tasks) as t:
473            if t: unittest(['--suite', 'jvmci', '--enable-timing', '--verbose', '--fail-fast'])
474
475    # Build the other VM flavors
476    with Task('BuildHotSpotJVMCIOthers: fastdebug', tasks) as t:
477        if t: _runmultimake(['--jdk-jvm-variants', 'server', '--jdk-debug-levels', 'fastdebug'])
478
479    with Task('CleanAndBuildIdealGraphVisualizer', tasks, disableJacoco=True) as t:
480        if t and platform.processor() != 'sparc':
481            buildxml = mx._cygpathU2W(join(_suite.dir, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml'))
482            mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=_igvBuildEnv())
483
484mx_gate.add_gate_runner(_suite, _jvmci_gate_runner)
485mx_gate.add_gate_argument('-g', '--only-build-jvmci', action='store_false', dest='buildNonJVMCI', help='only build the JVMCI VM')
486
487def _igvJdk():
488    v8u20 = mx.VersionSpec("1.8.0_20")
489    v8u40 = mx.VersionSpec("1.8.0_40")
490    v8 = mx.VersionSpec("1.8")
491    def _igvJdkVersionCheck(version):
492        return version >= v8 and (version < v8u20 or version >= v8u40)
493    return mx.get_jdk(_igvJdkVersionCheck, versionDescription='>= 1.8 and < 1.8.0u20 or >= 1.8.0u40', purpose="building & running IGV").home
494
495def _igvBuildEnv():
496        # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs
497    env = dict(os.environ)
498    proxy = os.environ.get('http_proxy')
499    if not (proxy is None) and len(proxy) > 0:
500        if '://' in proxy:
501            # Remove the http:// prefix (or any other protocol prefix)
502            proxy = proxy.split('://', 1)[1]
503        # Separate proxy server name and port number
504        proxyName, proxyPort = proxy.split(':', 1)
505        proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort
506        env['ANT_OPTS'] = proxyEnv
507
508    env['JAVA_HOME'] = _igvJdk()
509    return env
510
511def igv(args):
512    """run the Ideal Graph Visualizer"""
513    logFile = '.ideal_graph_visualizer.log'
514    with open(join(_suite.dir, logFile), 'w') as fp:
515        mx.logv('[Ideal Graph Visualizer log is in ' + fp.name + ']')
516        nbplatform = join(_suite.dir, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'nbplatform')
517
518        # Remove NetBeans platform if it is earlier than the current supported version
519        if exists(nbplatform):
520            updateTrackingFile = join(nbplatform, 'platform', 'update_tracking', 'org-netbeans-core.xml')
521            if not exists(updateTrackingFile):
522                mx.log('Could not find \'' + updateTrackingFile + '\', removing NetBeans platform')
523                shutil.rmtree(nbplatform)
524            else:
525                dom = xml.dom.minidom.parse(updateTrackingFile)
526                currentVersion = mx.VersionSpec(dom.getElementsByTagName('module_version')[0].getAttribute('specification_version'))
527                supportedVersion = mx.VersionSpec('3.43.1')
528                if currentVersion < supportedVersion:
529                    mx.log('Replacing NetBeans platform version ' + str(currentVersion) + ' with version ' + str(supportedVersion))
530                    shutil.rmtree(nbplatform)
531                elif supportedVersion < currentVersion:
532                    mx.log('Supported NetBeans version in igv command should be updated to ' + str(currentVersion))
533
534        if not exists(nbplatform):
535            mx.logv('[This execution may take a while as the NetBeans platform needs to be downloaded]')
536
537        env = _igvBuildEnv()
538        # make the jar for Batik 1.7 available.
539        env['IGV_BATIK_JAR'] = mx.library('BATIK').get_path(True)
540        if mx.run(['ant', '-f', mx._cygpathU2W(join(_suite.dir, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml')), '-l', mx._cygpathU2W(fp.name), 'run'], env=env, nonZeroIsFatal=False):
541            mx.abort("IGV ant build & launch failed. Check '" + logFile + "'. You can also try to delete 'src/share/tools/IdealGraphVisualizer/nbplatform'.")
542
543def c1visualizer(args):
544    """run the Cl Compiler Visualizer"""
545    libpath = join(_suite.dir, 'lib')
546    if mx.get_os() == 'windows':
547        executable = join(libpath, 'c1visualizer', 'bin', 'c1visualizer.exe')
548    else:
549        executable = join(libpath, 'c1visualizer', 'bin', 'c1visualizer')
550
551    # Check whether the current C1Visualizer installation is the up-to-date
552    if exists(executable) and not exists(mx.library('C1VISUALIZER_DIST').get_path(resolve=False)):
553        mx.log('Updating C1Visualizer')
554        shutil.rmtree(join(libpath, 'c1visualizer'))
555
556    archive = mx.library('C1VISUALIZER_DIST').get_path(resolve=True)
557
558    if not exists(executable):
559        zf = zipfile.ZipFile(archive, 'r')
560        zf.extractall(libpath)
561
562    if not exists(executable):
563        mx.abort('C1Visualizer binary does not exist: ' + executable)
564
565    if mx.get_os() != 'windows':
566        # Make sure that execution is allowed. The zip file does not always specfiy that correctly
567        os.chmod(executable, 0777)
568
569    mx.run([executable])
570
571def hsdis(args, copyToDir=None):
572    """download the hsdis library
573
574    This is needed to support HotSpot's assembly dumping features.
575    By default it downloads the Intel syntax version, use the 'att' argument to install AT&T syntax."""
576    flavor = 'intel'
577    if 'att' in args:
578        flavor = 'att'
579    if mx.get_arch() == "sparcv9":
580        flavor = "sparcv9"
581    lib = mx.add_lib_suffix('hsdis-' + mx.get_arch())
582    path = join(_suite.dir, 'lib', lib)
583
584    sha1s = {
585        'att/hsdis-amd64.dll' : 'bcbd535a9568b5075ab41e96205e26a2bac64f72',
586        'att/hsdis-amd64.so' : '58919ba085d4ef7a513f25bae75e7e54ee73c049',
587        'intel/hsdis-amd64.dll' : '6a388372cdd5fe905c1a26ced614334e405d1f30',
588        'intel/hsdis-amd64.so' : '844ed9ffed64fe9599638f29a8450c50140e3192',
589        'intel/hsdis-amd64.dylib' : 'fdb13ef0d7d23d93dacaae9c98837bea0d4fc5a2',
590        'sparcv9/hsdis-sparcv9.so': '970640a9af0bd63641f9063c11275b371a59ee60',
591    }
592
593    flavoredLib = flavor + "/" + lib
594    if flavoredLib not in sha1s:
595        mx.logv("hsdis not supported on this plattform or architecture")
596        return
597
598    if not exists(path):
599        sha1 = sha1s[flavoredLib]
600        sha1path = path + '.sha1'
601        mx.download_file_with_sha1('hsdis', path, ['https://lafo.ssw.uni-linz.ac.at/pub/hsdis/' + flavoredLib], sha1, sha1path, True, True, sources=False)
602    if copyToDir is not None and exists(copyToDir):
603        shutil.copy(path, copyToDir)
604
605def hcfdis(args):
606    """disassemble HexCodeFiles embedded in text files
607
608    Run a tool over the input files to convert all embedded HexCodeFiles
609    to a disassembled format."""
610
611    parser = ArgumentParser(prog='mx hcfdis')
612    parser.add_argument('-m', '--map', help='address to symbol map applied to disassembler output')
613    parser.add_argument('files', nargs=REMAINDER, metavar='files...')
614
615    args = parser.parse_args(args)
616
617    path = mx.library('HCFDIS').get_path(resolve=True)
618    mx.run_java(['-cp', path, 'com.oracle.max.hcfdis.HexCodeFileDis'] + args.files)
619
620    if args.map is not None:
621        addressRE = re.compile(r'0[xX]([A-Fa-f0-9]+)')
622        with open(args.map) as fp:
623            lines = fp.read().splitlines()
624        symbols = dict()
625        for l in lines:
626            addressAndSymbol = l.split(' ', 1)
627            if len(addressAndSymbol) == 2:
628                address, symbol = addressAndSymbol
629                if address.startswith('0x'):
630                    address = long(address, 16)
631                    symbols[address] = symbol
632        for f in args.files:
633            with open(f) as fp:
634                lines = fp.read().splitlines()
635            updated = False
636            for i in range(0, len(lines)):
637                l = lines[i]
638                for m in addressRE.finditer(l):
639                    sval = m.group(0)
640                    val = long(sval, 16)
641                    sym = symbols.get(val)
642                    if sym:
643                        l = l.replace(sval, sym)
644                        updated = True
645                        lines[i] = l
646            if updated:
647                mx.log('updating ' + f)
648                with open('new_' + f, "w") as fp:
649                    for l in lines:
650                        print >> fp, l
651
652def jol(args):
653    """Java Object Layout"""
654    joljar = mx.library('JOL_INTERNALS').get_path(resolve=True)
655    candidates = mx.findclass(args, logToConsole=False, matcher=lambda s, classname: s == classname or classname.endswith('.' + s) or classname.endswith('$' + s))
656
657    if len(candidates) > 0:
658        candidates = mx.select_items(sorted(candidates))
659    else:
660        # mx.findclass can be mistaken, don't give up yet
661        candidates = args
662
663    run_vm(['-javaagent:' + joljar, '-cp', os.pathsep.join([mx.classpath(), joljar]), "org.openjdk.jol.MainObjectInternals"] + candidates)
664
665class JVMCIArchiveParticipant:
666    def __init__(self, dist):
667        self.dist = dist
668
669    def __opened__(self, arc, srcArc, services):
670        self.services = services
671        self.arc = arc
672
673    def __add__(self, arcname, contents):
674        if arcname.startswith('META-INF/jvmci.providers/'):
675            provider = arcname[len('META-INF/jvmci.providers/'):]
676            for service in contents.strip().split(os.linesep):
677                assert service
678                self.services.setdefault(service, []).append(provider)
679            return True
680        return False
681
682    def __addsrc__(self, arcname, contents):
683        return False
684
685    def __closing__(self):
686        pass
687
688def _get_openjdk_os():
689    # See: common/autoconf/platform.m4
690    os = mx.get_os()
691    if 'darwin' in os:
692        os = 'macosx'
693    elif 'linux' in os:
694        os = 'linux'
695    elif 'solaris' in os:
696        os = 'solaris'
697    elif 'cygwin' in os or 'mingw' in os:
698        os = 'windows'
699    return os
700
701def _get_openjdk_cpu():
702    cpu = mx.get_arch()
703    if cpu == 'amd64':
704        cpu = 'x86_64'
705    elif cpu == 'sparcv9':
706        cpu = 'sparcv9'
707    return cpu
708
709def _get_openjdk_os_cpu():
710    return _get_openjdk_os() + '-' + _get_openjdk_cpu()
711
712def _get_jdk_build_dir(debugLevel=None):
713    """
714    Gets the directory into which the JDK is built. This directory contains
715    the exploded JDK under jdk/ and the JDK image under images/jdk/.
716    """
717    if debugLevel is None:
718        debugLevel = _vm.debugLevel
719    name = '{}-{}-{}-{}'.format(_get_openjdk_os_cpu(), 'normal', _vm.jvmVariant, debugLevel)
720    return join(dirname(_suite.dir), 'build', name)
721
722_jvmci_bootclasspath_prepends = []
723
724def _get_hotspot_build_dir(jvmVariant=None, debugLevel=None):
725    """
726    Gets the directory in which a particular HotSpot configuration is built
727    (e.g., <JDK_REPO_ROOT>/build/macosx-x86_64-normal-server-release/hotspot/bsd_amd64_compiler2)
728    """
729    if jvmVariant is None:
730        jvmVariant = _vm.jvmVariant
731
732    os = mx.get_os()
733    if os == 'darwin':
734        os = 'bsd'
735    arch = mx.get_arch()
736    buildname = {'client': 'compiler1', 'server': 'compiler2'}.get(jvmVariant, jvmVariant)
737
738    name = '{}_{}_{}'.format(os, arch, buildname)
739    return join(_get_jdk_build_dir(debugLevel=debugLevel), 'hotspot', name)
740
741def add_bootclasspath_prepend(dep):
742    assert isinstance(dep, mx.ClasspathDependency)
743    _jvmci_bootclasspath_prepends.append(dep)
744
745class JVMCI9JDKConfig(mx.JDKConfig):
746    def __init__(self, debugLevel):
747        self.debugLevel = debugLevel
748        jdkBuildDir = _get_jdk_build_dir(debugLevel)
749        jdkDir = join(jdkBuildDir, 'images', 'jdk') if mx.get_opts().use_jdk_image else join(jdkBuildDir, 'jdk')
750        mx.JDKConfig.__init__(self, jdkDir, tag=_JVMCI_JDK_TAG)
751
752    def parseVmArgs(self, args, addDefaultArgs=True):
753        args = mx.expand_project_in_args(args, insitu=False)
754        jacocoArgs = mx_gate.get_jacoco_agent_args()
755        if jacocoArgs:
756            args = jacocoArgs + args
757
758        args = ['-Xbootclasspath/p:' + dep.classpath_repr() for dep in _jvmci_bootclasspath_prepends] + args
759
760        jvmciModeArgs = _jvmciModes[_vm.jvmciMode]
761        if jvmciModeArgs:
762            bcpDeps = [jdkDist.dist() for jdkDist in jdkDeployedDists]
763            if bcpDeps:
764                args = ['-Xbootclasspath/p:' + os.pathsep.join([d.classpath_repr() for d in bcpDeps])] + args
765
766        # Set the default JVMCI compiler
767        for jdkDist in reversed(jdkDeployedDists):
768            assert isinstance(jdkDist, JvmciJDKDeployedDist), jdkDist
769            if jdkDist._compilers:
770                jvmciCompiler = jdkDist._compilers[-1]
771                args = ['-Djvmci.compiler=' + jvmciCompiler] + args
772                break
773
774        if '-version' in args:
775            ignoredArgs = args[args.index('-version') + 1:]
776            if  len(ignoredArgs) > 0:
777                mx.log("Warning: The following options will be ignored by the vm because they come after the '-version' argument: " + ' '.join(ignoredArgs))
778        return self.processArgs(args, addDefaultArgs=addDefaultArgs)
779
780    # Overrides JDKConfig
781    def run_java(self, args, vm=None, nonZeroIsFatal=True, out=None, err=None, cwd=None, timeout=None, env=None, addDefaultArgs=True):
782        if vm is None:
783            vm = 'server'
784
785        args = self.parseVmArgs(args, addDefaultArgs=addDefaultArgs)
786
787        jvmciModeArgs = _jvmciModes[_vm.jvmciMode]
788        cmd = [self.java] + ['-' + vm] + jvmciModeArgs + args
789        return mx.run(cmd, nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd)
790
791"""
792The dict of JVMCI JDKs indexed by debug-level names.
793"""
794_jvmci_jdks = {}
795
796def get_jvmci_jdk(debugLevel=None):
797    """
798    Gets the JVMCI JDK corresponding to 'debugLevel'.
799    """
800    if not debugLevel:
801        debugLevel = _vm.debugLevel
802    jdk = _jvmci_jdks.get(debugLevel)
803    if jdk is None:
804        try:
805            jdk = JVMCI9JDKConfig(debugLevel)
806        except mx.JDKConfigException as e:
807            jdkBuildDir = _get_jdk_build_dir(debugLevel)
808            msg = 'Error with the JDK built into {}:\n{}\nTry (re)building it with: mx --jdk-debug-level={} make'
809            if mx.get_opts().use_jdk_image:
810                msg += ' images'
811            mx.abort(msg.format(jdkBuildDir, e.message, debugLevel))
812        _jvmci_jdks[debugLevel] = jdk
813    return jdk
814
815class JVMCIJDKFactory(mx.JDKFactory):
816    def getJDKConfig(self):
817        jdk = get_jvmci_jdk(_vm.debugLevel)
818        return jdk
819
820    def description(self):
821        return "JVMCI JDK"
822
823mx.update_commands(_suite, {
824    'make': [_runmake, '[args...]', _makehelp],
825    'multimake': [_runmultimake, '[options]'],
826    'c1visualizer' : [c1visualizer, ''],
827    'hsdis': [hsdis, '[att]'],
828    'hcfdis': [hcfdis, ''],
829    'igv' : [igv, ''],
830    'jol' : [jol, ''],
831    'vm': [run_vm, '[-options] class [args...]'],
832})
833
834mx.add_argument('-M', '--jvmci-mode', action='store', choices=sorted(_jvmciModes.viewkeys()), help='the JVM variant type to build/run (default: ' + _vm.jvmciMode + ')')
835mx.add_argument('--jdk-jvm-variant', '--vm', action='store', choices=_jdkJvmVariants + sorted(_legacyVms.viewkeys()), help='the JVM variant type to build/run (default: ' + _vm.jvmVariant + ')')
836mx.add_argument('--jdk-debug-level', '--vmbuild', action='store', choices=_jdkDebugLevels + sorted(_legacyVmbuilds.viewkeys()), help='the JDK debug level to build/run (default: ' + _vm.debugLevel + ')')
837mx.add_argument('-I', '--use-jdk-image', action='store_true', help='build/run JDK image instead of exploded JDK')
838
839def mx_post_parse_cmd_line(opts):
840    mx.addJDKFactory(_JVMCI_JDK_TAG, mx.JavaCompliance('9'), JVMCIJDKFactory())
841    mx.set_java_command_default_jdk_tag(_JVMCI_JDK_TAG)
842
843    jdkTag = mx.get_jdk_option().tag
844
845    jvmVariant = None
846    debugLevel = None
847    jvmciMode = None
848
849    if opts.jdk_jvm_variant is not None:
850        jvmVariant = opts.jdk_jvm_variant
851        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
852            mx.warn('Ignoring "--jdk-jvm-variant" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
853
854    if opts.jdk_debug_level is not None:
855        debugLevel = _translateLegacyDebugLevel(opts.jdk_debug_level)
856        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
857            mx.warn('Ignoring "--jdk-debug-level" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
858
859    if opts.jvmci_mode is not None:
860        jvmciMode = opts.jvmci_mode
861        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
862            mx.warn('Ignoring "--jvmci-mode" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
863
864    _vm.update(jvmVariant, debugLevel, jvmciMode)
865
866    for jdkDist in jdkDeployedDists:
867        dist = jdkDist.dist()
868        if isinstance(jdkDist, JvmciJDKDeployedDist):
869            dist.set_archiveparticipant(JVMCIArchiveParticipant(dist))
870