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