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