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