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