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