mx_jvmci.py revision 10019:2c4e0146b775
1189251Ssam#
2189251Ssam# ----------------------------------------------------------------------------------------------------
3281806Srpaulo#
4189251Ssam# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
5252726Srpaulo# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6252726Srpaulo#
7189251Ssam# This code is free software; you can redistribute it and/or modify it
8189251Ssam# under the terms of the GNU General Public License version 2 only, as
9189251Ssam# published by the Free Software Foundation.
10189251Ssam#
11189251Ssam# This code is distributed in the hope that it will be useful, but WITHOUT
12189251Ssam# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13281806Srpaulo# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14189251Ssam# version 2 for more details (a copy is included in the LICENSE file that
15189251Ssam# accompanied this code).
16189251Ssam#
17189251Ssam# You should have received a copy of the GNU General Public License version
18189251Ssam# 2 along with this work; if not, write to the Free Software Foundation,
19189251Ssam# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20189251Ssam#
21189251Ssam# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22189251Ssam# or visit www.oracle.com if you need additional information or have any
23189251Ssam# questions.
24189251Ssam#
25189251Ssam# ----------------------------------------------------------------------------------------------------
26189251Ssam
27189251Ssamimport os, shutil, zipfile, re, time, sys, datetime, platform
28189251Ssamfrom os.path import join, exists, dirname, isdir
29189251Ssamfrom argparse import ArgumentParser, REMAINDER
30189251Ssamimport StringIO
31189251Ssamimport xml.dom.minidom
32189251Ssamimport subprocess
33189251Ssam
34189251Ssamimport mx
35189251Ssamimport mx_gate
36189251Ssamimport mx_unittest
37189251Ssam
38189251Ssamfrom mx_gate import Task
39189251Ssamfrom mx_unittest import unittest
40189251Ssam
41189251Ssam_suite = mx.suite('jvmci')
42189251Ssam
43189251Ssam"""
44189251SsamTop level directory of the JDK source workspace.
45189251Ssam"""
46189251Ssam_jdkSourceRoot = dirname(_suite.dir)
47189251Ssam
48189251Ssam_JVMCI_JDK_TAG = 'jvmci'
49189251Ssam
50189251Ssam_minVersion = mx.VersionSpec('1.9')
51189251Ssam
52189251Ssam# max version (first _unsupported_ version)
53189251Ssam_untilVersion = None
54189251Ssam
55189251Ssam_jvmciModes = {
56189251Ssam    'hosted' : ['-XX:+UnlockExperimentalVMOptions', '-XX:+EnableJVMCI'],
57189251Ssam    'jit' : ['-XX:+UnlockExperimentalVMOptions', '-XX:+EnableJVMCI', '-XX:+UseJVMCICompiler'],
58189251Ssam    'disabled' : []
59189251Ssam}
60189251Ssam
61189251Ssam# TODO: can optimized be built without overriding release build?
62189251Ssam_jdkDebugLevels = ['release', 'fastdebug', 'slowdebug']
63189251Ssam
64189251Ssam# TODO: add client once/if it can be built on 64-bit platforms
65189251Ssam_jdkJvmVariants = ['server']
66189251Ssam
67189251Ssam"""
68189251SsamTranslation table from mx_jvmci:8 --vmbuild values to mx_jvmci:9 --jdk-debug-level values.
69189251Ssam"""
70189251Ssam_legacyVmbuilds = {
71189251Ssam    'product' : 'release',
72189251Ssam    'debug' : 'slowdebug'
73189251Ssam}
74189251Ssam
75189251Ssam"""
76189251SsamTranslates a mx_jvmci:8 --vmbuild value to a mx_jvmci:9 --jdk-debug-level value.
77189251Ssam"""
78189251Ssamdef _translateLegacyDebugLevel(debugLevel):
79189251Ssam    return _legacyVmbuilds.get(debugLevel, debugLevel)
80189251Ssam
81189251Ssam"""
82189251SsamTranslation table from mx_jvmci:8 --vm values to mx_jvmci:9 (--jdk-jvm-variant, --jvmci-mode) tuples.
83189251Ssam"""
84189251Ssam_legacyVms = {
85189251Ssam    'jvmci' : ('server', 'jit')
86189251Ssam}
87189251Ssam
88189251Ssam"""
89189251SsamA VM configuration composed of a JDK debug level, JVM variant and a JVMCI mode.
90189251SsamThis is also a context manager that can be used with the 'with' statement to set/change
91189251Ssama VM configuration within a dynamic scope. For example:
92189251Ssam
93189251Ssam    with ConfiguredJDK(debugLevel='fastdebug'):
94189251Ssam        dacapo(['pmd'])
95189251Ssam"""
96189251Ssamclass VM:
97189251Ssam    def __init__(self, jvmVariant=None, debugLevel=None, jvmciMode=None):
98189251Ssam        self.update(jvmVariant, debugLevel, jvmciMode)
99189251Ssam
100189251Ssam    def update(self, jvmVariant=None, debugLevel=None, jvmciMode=None):
101189251Ssam        if jvmVariant in _legacyVms:
102189251Ssam            # Backwards compatibility for mx_jvmci:8 API
103189251Ssam            jvmVariant, newJvmciMode = _legacyVms[jvmVariant]
104189251Ssam            if jvmciMode is not None and jvmciMode != newJvmciMode:
105189251Ssam                mx.abort('JVM variant "' + jvmVariant + '" implies JVMCI mode "' + newJvmciMode +
106189251Ssam                         '" which conflicts with explicitly specified JVMCI mode of "' + jvmciMode + '"')
107189251Ssam            jvmciMode = newJvmciMode
108189251Ssam        debugLevel = _translateLegacyDebugLevel(debugLevel)
109189251Ssam        assert jvmVariant is None or jvmVariant in _jdkJvmVariants, jvmVariant
110189251Ssam        assert debugLevel is None or debugLevel in _jdkDebugLevels, debugLevel
111189251Ssam        assert jvmciMode is None or jvmciMode in _jvmciModes, jvmciMode
112189251Ssam        self.jvmVariant = jvmVariant or _vm.jvmVariant
113189251Ssam        self.debugLevel = debugLevel or _vm.debugLevel
114189251Ssam        self.jvmciMode = jvmciMode or _vm.jvmciMode
115189251Ssam
116189251Ssam    def __enter__(self):
117189251Ssam        global _vm
118189251Ssam        self.previousVm = _vm
119189251Ssam        _vm = self
120189251Ssam
121189251Ssam    def __exit__(self, exc_type, exc_value, traceback):
122189251Ssam        global _vm
123189251Ssam        _vm = self.previousVm
124189251Ssam
125189251Ssam_vm = VM(jvmVariant=_jdkJvmVariants[0], debugLevel=_jdkDebugLevels[0], jvmciMode='hosted')
126189251Ssam
127189251Ssamdef get_vm():
128189251Ssam    """
129189251Ssam    Gets the configured VM.
130189251Ssam    """
131189251Ssam    return _vm
132189251Ssam
133189251Ssamdef relativeVmLibDirInJdk():
134189251Ssam    mxos = mx.get_os()
135189251Ssam    if mxos == 'darwin':
136189251Ssam        return join('lib')
137189251Ssam    if mxos == 'windows' or mxos == 'cygwin':
138189251Ssam        return join('bin')
139189251Ssam    return join('lib', mx.get_arch())
140189251Ssam
141189251Ssamdef isJVMCIEnabled(vm):
142189251Ssam    assert vm in _jdkJvmVariants
143189251Ssam    return True
144189251Ssam
145189251Ssamclass JvmciJDKDeployedDist(object):
146189251Ssam    def __init__(self, name, compilers=False):
147189251Ssam        self._name = name
148189251Ssam        self._compilers = compilers
149189251Ssam
150189251Ssam    def dist(self):
151189251Ssam        return mx.distribution(self._name)
152189251Ssam
153189251Ssam    def deploy(self, jdkDir):
154189251Ssam        mx.nyi('deploy', self)
155189251Ssam
156189251Ssam    def post_parse_cmd_line(self):
157189251Ssam        self.set_archiveparticipant()
158189251Ssam
159189251Ssam    def set_archiveparticipant(self):
160189251Ssam        dist = self.dist()
161189251Ssam        dist.set_archiveparticipant(JVMCIArchiveParticipant(dist))
162189251Ssam
163189251Ssamclass ExtJDKDeployedDist(JvmciJDKDeployedDist):
164189251Ssam    def __init__(self, name):
165189251Ssam        JvmciJDKDeployedDist.__init__(self, name)
166189251Ssam
167189251Ssam
168189251Ssam"""
169189251SsamThe monolithic JVMCI distribution is deployed through use of -Xbootclasspath/p
170189251Ssamso that it's not necessary to run JDK make after editing JVMCI sources.
171189251SsamThe latter causes all JDK Java sources to be rebuilt since JVMCI is
172189251Ssam(currently) in java.base.
173189251Ssam"""
174189251Ssam_monolithicJvmci = JvmciJDKDeployedDist('JVMCI')
175189251Ssam
176189251Ssam"""
177189251SsamList of distributions that are deployed on the boot class path.
178189251SsamNote: In jvmci-8, they were deployed directly into the JDK directory.
179189251Ssam"""
180189251SsamjdkDeployedDists = [_monolithicJvmci]
181189251Ssam
182189251Ssamdef _makehelp():
183189251Ssam    return subprocess.check_output([mx.gmake_cmd(), 'help'], cwd=_jdkSourceRoot)
184189251Ssam
185189251Ssamdef _runmake(args):
186189251Ssam    """run the JDK make process
187189251Ssam
188189251SsamTo build hotspot and import it into the JDK: "mx make hotspot import-hotspot"
189189251Ssam{0}"""
190189251Ssam
191189251Ssam    jdkBuildDir = _get_jdk_build_dir()
192189251Ssam    if not exists(jdkBuildDir):
193189251Ssam        # JDK9 must be bootstrapped with a JDK8
194189251Ssam        compliance = mx.JavaCompliance('8')
195189251Ssam        jdk8 = mx.get_jdk(compliance.exactMatch, versionDescription=compliance.value)
196189251Ssam        cmd = ['sh', 'configure', '--with-debug-level=' + _vm.debugLevel, '--disable-debug-symbols', '--disable-precompiled-headers',
197189251Ssam               '--with-jvm-variants=' + _vm.jvmVariant, '--disable-warnings-as-errors', '--with-boot-jdk=' + jdk8.home]
198189251Ssam        mx.run(cmd, cwd=_jdkSourceRoot)
199189251Ssam    cmd = [mx.gmake_cmd(), 'CONF=' + _vm.debugLevel]
200189251Ssam    if mx.get_opts().verbose:
201189251Ssam        cmd.append('LOG=debug')
202189251Ssam    cmd.extend(args)
203189251Ssam    if mx.get_opts().use_jdk_image and 'images' not in args:
204189251Ssam        cmd.append('images')
205189251Ssam
206189251Ssam    if not mx.get_opts().verbose:
207189251Ssam        mx.log('--------------- make execution ----------------------')
208189251Ssam        mx.log('Working directory: ' + _jdkSourceRoot)
209189251Ssam        mx.log('Command line: ' + ' '.join(cmd))
210189251Ssam        mx.log('-----------------------------------------------------')
211189251Ssam
212189251Ssam    mx.run(cmd, cwd=_jdkSourceRoot)
213189251Ssam
214189251Ssam    if 'images' in cmd:
215189251Ssam        _create_jdk_bundle(jdkBuildDir)
216189251Ssam
217189251Ssamdef _get_jdk_bundle_arches():
218189251Ssam    """
219189251Ssam    Gets a list of names that will be the part of a JDK bundle's file name denoting the architecture.
220189251Ssam    The first element in the list is the canonical name. Symlinks should be created for the
221189251Ssam    remaining names.
222189251Ssam    """
223189251Ssam    cpu = mx.get_arch()
224189251Ssam    if cpu == 'amd64':
225189251Ssam        return ['x64', 'x86_64', 'amd64']
226189251Ssam    elif cpu == 'sparcv9':
227189251Ssam        return ['sparcv9']
228189251Ssam    mx.abort('Unsupported JDK bundle arch: ' + cpu)
229189251Ssam
230189251Ssamdef _create_jdk_bundle(jdkBuildDir):
231189251Ssam    """
232189251Ssam    Creates a tar.gz JDK archive, an accompanying tar.gz.sha1 file with its
233189251Ssam    SHA1 signature plus symlinks to the archive for non-canonical architecture names.
234189251Ssam    """
235189251Ssam    jdkImageDir = join(jdkBuildDir, 'images', 'jdk')
236189251Ssam
237189251Ssam    arches = _get_jdk_bundle_arches()
238189251Ssam    jdkTgzPath = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arches[0]))
239189251Ssam    with mx.Archiver(jdkTgzPath, kind='tgz') as arc:
240189251Ssam        mx.log('Creating ' + jdkTgzPath)
241189251Ssam        for root, _, filenames in os.walk(jdkImageDir):
242189251Ssam            for name in filenames:
243189251Ssam                f = join(root, name)
244189251Ssam                arcname = 'jdk1.9.0/' + os.path.relpath(f, jdkImageDir)
245189251Ssam                arc.zf.add(name=f, arcname=arcname, recursive=False)
246189251Ssam        # The OpenJDK build creates an empty cacerts file so grab one from
247189251Ssam        # the default JDK which is assumed to be an OracleJDK
248189251Ssam        cacerts = join(mx.get_jdk(tag='default').home, 'jre', 'lib', 'security', 'cacerts')
249189251Ssam        arc.zf.add(name=cacerts, arcname='jdk1.9.0/lib/security/cacerts')
250189251Ssam
251189251Ssam    with open(jdkTgzPath + '.sha1', 'w') as fp:
252189251Ssam        mx.log('Creating ' + jdkTgzPath + '.sha1')
253189251Ssam        fp.write(mx.sha1OfFile(jdkTgzPath))
254189251Ssam
255189251Ssam    def _create_link(source, link_name):
256189251Ssam        if exists(link_name):
257189251Ssam            os.remove(link_name)
258189251Ssam        mx.log('Creating ' + link_name + ' -> ' + source)
259189251Ssam        os.symlink(source, link_name)
260189251Ssam
261189251Ssam    for arch in arches[1:]:
262189251Ssam        link_name = join(_suite.get_output_root(), 'jdk-bundles', 'jdk9-{}-{}.tar.gz'.format(_get_openjdk_os(), arch))
263189251Ssam        jdkTgzName = os.path.basename(jdkTgzPath)
264189251Ssam        _create_link(jdkTgzName, link_name)
265281806Srpaulo        _create_link(jdkTgzName + '.sha1', link_name + '.sha1')
266281806Srpaulo
267281806Srpaulodef _runmultimake(args):
268281806Srpaulo    """run the JDK make process for one or more configurations"""
269281806Srpaulo
270281806Srpaulo    jvmVariantsDefault = ','.join(_jdkJvmVariants)
271281806Srpaulo    debugLevelsDefault = ','.join(_jdkDebugLevels)
272281806Srpaulo
273281806Srpaulo    parser = ArgumentParser(prog='mx multimake')
274281806Srpaulo    parser.add_argument('--jdk-jvm-variants', '--vms', help='a comma separated list of VMs to build (default: ' + jvmVariantsDefault + ')', metavar='<args>', default=jvmVariantsDefault)
275281806Srpaulo    parser.add_argument('--jdk-debug-levels', '--builds', help='a comma separated list of JDK debug levels (default: ' + debugLevelsDefault + ')', metavar='<args>', default=debugLevelsDefault)
276281806Srpaulo    parser.add_argument('-n', '--no-check', action='store_true', help='omit running "java -version" after each build')
277281806Srpaulo    select = parser.add_mutually_exclusive_group()
278189251Ssam    select.add_argument('-c', '--console', action='store_true', help='send build output to console instead of log files')
279189251Ssam    select.add_argument('-d', '--output-dir', help='directory for log files instead of current working directory', default=os.getcwd(), metavar='<dir>')
280189251Ssam
281281806Srpaulo    args = parser.parse_args(args)
282281806Srpaulo    jvmVariants = args.jdk_jvm_variants.split(',')
283281806Srpaulo    debugLevels = [_translateLegacyDebugLevel(dl) for dl in args.jdk_debug_levels.split(',')]
284281806Srpaulo
285281806Srpaulo    allStart = time.time()
286281806Srpaulo    for jvmVariant in jvmVariants:
287281806Srpaulo        for debugLevel in debugLevels:
288281806Srpaulo            if not args.console:
289281806Srpaulo                logFile = join(mx.ensure_dir_exists(args.output_dir), jvmVariant + '-' + debugLevel + '.log')
290281806Srpaulo                log = open(logFile, 'wb')
291189251Ssam                start = time.time()
292189251Ssam                mx.log('BEGIN: ' + jvmVariant + '-' + debugLevel + '\t(see: ' + logFile + ')')
293189251Ssam                verbose = ['-v'] if mx.get_opts().verbose else []
294189251Ssam                # Run as subprocess so that output can be directed to a file
295189251Ssam                cmd = [sys.executable, '-u', mx.__file__] + verbose + ['--jdk-jvm-variant=' + jvmVariant, '--jdk-debug-level=' + debugLevel, 'make']
296189251Ssam                mx.logv("executing command: " + str(cmd))
297189251Ssam                subprocess.check_call(cmd, cwd=_suite.dir, stdout=log, stderr=subprocess.STDOUT)
298189251Ssam                duration = datetime.timedelta(seconds=time.time() - start)
299189251Ssam                mx.log('END:   ' + jvmVariant + '-' + debugLevel + '\t[' + str(duration) + ']')
300189251Ssam            else:
301189251Ssam                with VM(jvmVariant=jvmVariant, debugLevel=debugLevel):
302189251Ssam                    _runmake([])
303189251Ssam            if not args.no_check:
304189251Ssam                with VM(jvmciMode='jit'):
305189251Ssam                    run_vm(['-XX:-BootstrapJVMCI', '-version'])
306189251Ssam    allDuration = datetime.timedelta(seconds=time.time() - allStart)
307189251Ssam    mx.log('TOTAL TIME:   ' + '[' + str(allDuration) + ']')
308189251Ssam
309189251Ssamclass HotSpotProject(mx.NativeProject):
310189251Ssam    """
311189251Ssam    Defines a NativeProject representing the HotSpot binaries built via make.
312189251Ssam    """
313189251Ssam    def __init__(self, suite, name, deps, workingSets, **args):
314189251Ssam        assert name == 'hotspot'
315189251Ssam        mx.NativeProject.__init__(self, suite, name, "", [], deps, workingSets, None, None, join(suite.mxDir, name))
316189251Ssam
317189251Ssam    def eclipse_config_up_to_date(self, configZip):
318189251Ssam        # Assume that any change to this module might imply changes to the generated IDE files
319189251Ssam        if configZip.isOlderThan(__file__):
320189251Ssam            return False
321189251Ssam        for _, source in self._get_eclipse_settings_sources().iteritems():
322189251Ssam            if configZip.isOlderThan(source):
323189251Ssam                return False
324189251Ssam        return True
325281806Srpaulo
326281806Srpaulo    def _get_eclipse_settings_sources(self):
327189251Ssam        """
328189251Ssam        Gets a dictionary from the name of an Eclipse settings file to
329189251Ssam        the file providing its generated content.
330189251Ssam        """
331189251Ssam        if not hasattr(self, '_eclipse_settings'):
332189251Ssam            esdict = {}
333189251Ssam            templateSettingsDir = join(self.dir, 'templates', 'eclipse', 'settings')
334189251Ssam            if exists(templateSettingsDir):
335281806Srpaulo                for name in os.listdir(templateSettingsDir):
336281806Srpaulo                    source = join(templateSettingsDir, name)
337189251Ssam                    esdict[name] = source
338189251Ssam            self._eclipse_settings = esdict
339189251Ssam        return self._eclipse_settings
340189251Ssam
341189251Ssam    def _eclipseinit(self, files=None, libFiles=None):
342189251Ssam        """
343189251Ssam        Generates an Eclipse project for each HotSpot build configuration.
344189251Ssam        """
345189251Ssam
346189251Ssam        roots = [
347189251Ssam            'ASSEMBLY_EXCEPTION',
348252726Srpaulo            'LICENSE',
349189251Ssam            'README',
350189251Ssam            'THIRD_PARTY_README',
351189251Ssam            'agent',
352189251Ssam            'make',
353189251Ssam            'src',
354189251Ssam            'test'
355189251Ssam        ]
356189251Ssam
357189251Ssam        for jvmVariant in _jdkJvmVariants:
358189251Ssam            for debugLevel in _jdkDebugLevels:
359189251Ssam                name = jvmVariant + '-' + debugLevel
360189251Ssam                eclProjectDir = join(self.dir, 'eclipse', name)
361281806Srpaulo                mx.ensure_dir_exists(eclProjectDir)
362252726Srpaulo
363252726Srpaulo                out = mx.XMLDoc()
364252726Srpaulo                out.open('projectDescription')
365281806Srpaulo                out.element('name', data='hotspot:' + name)
366281806Srpaulo                out.element('comment', data='')
367281806Srpaulo                out.element('projects', data='')
368281806Srpaulo                out.open('buildSpec')
369281806Srpaulo                out.open('buildCommand')
370281806Srpaulo                out.element('name', data='org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder')
371189251Ssam                out.element('triggers', data='full,incremental')
372189251Ssam                out.element('arguments', data='')
373189251Ssam                out.close('buildCommand')
374189251Ssam
375189251Ssam                out.close('buildSpec')
376189251Ssam                out.open('natures')
377189251Ssam                out.element('nature', data='org.eclipse.cdt.core.cnature')
378189251Ssam                out.element('nature', data='org.eclipse.cdt.core.ccnature')
379189251Ssam                out.element('nature', data='org.eclipse.cdt.managedbuilder.core.managedBuildNature')
380189251Ssam                out.element('nature', data='org.eclipse.cdt.managedbuilder.core.ScannerConfigNature')
381252726Srpaulo                out.close('natures')
382189251Ssam
383189251Ssam                if roots:
384189251Ssam                    out.open('linkedResources')
385189251Ssam                    for r in roots:
386189251Ssam                        f = join(_suite.dir, r)
387189251Ssam                        out.open('link')
388189251Ssam                        out.element('name', data=r)
389189251Ssam                        out.element('type', data='2' if isdir(f) else '1')
390                        out.element('locationURI', data=mx.get_eclipse_project_rel_locationURI(f, eclProjectDir))
391                        out.close('link')
392
393                    out.open('link')
394                    out.element('name', data='generated')
395                    out.element('type', data='2')
396                    generated = join(_get_hotspot_build_dir(jvmVariant, debugLevel), 'generated')
397                    out.element('locationURI', data=mx.get_eclipse_project_rel_locationURI(generated, eclProjectDir))
398                    out.close('link')
399
400                    out.close('linkedResources')
401                out.close('projectDescription')
402                projectFile = join(eclProjectDir, '.project')
403                mx.update_file(projectFile, out.xml(indent='\t', newl='\n'))
404                if files:
405                    files.append(projectFile)
406
407                cprojectTemplate = join(self.dir, 'templates', 'eclipse', 'cproject')
408                cprojectFile = join(eclProjectDir, '.cproject')
409                with open(cprojectTemplate) as f:
410                    content = f.read()
411                mx.update_file(cprojectFile, content)
412                if files:
413                    files.append(cprojectFile)
414
415                settingsDir = join(eclProjectDir, ".settings")
416                mx.ensure_dir_exists(settingsDir)
417                for name, source in self._get_eclipse_settings_sources().iteritems():
418                    out = StringIO.StringIO()
419                    print >> out, '# GENERATED -- DO NOT EDIT'
420                    print >> out, '# Source:', source
421                    with open(source) as f:
422                        print >> out, f.read()
423                    content = out.getvalue()
424                    mx.update_file(join(settingsDir, name), content)
425                    if files:
426                        files.append(join(settingsDir, name))
427
428    def getBuildTask(self, args):
429        return JDKBuildTask(self, args, _vm.debugLevel, _vm.jvmVariant)
430
431
432class JDKBuildTask(mx.NativeBuildTask):
433    def __init__(self, project, args, debugLevel, jvmVariant):
434        mx.NativeBuildTask.__init__(self, args, project)
435        self.jvmVariant = jvmVariant
436        self.debugLevel = debugLevel
437
438    def __str__(self):
439        return 'Building JDK[{}, {}]'.format(self.debugLevel, self.jvmVariant)
440
441    def build(self):
442        if mx.get_opts().use_jdk_image:
443            _runmake(['images'])
444        else:
445            _runmake([])
446        self._newestOutput = None
447
448    def clean(self, forBuild=False):
449        if forBuild:  # Let make handle incremental builds
450            return
451        if exists(_get_jdk_build_dir(self.debugLevel)):
452            _runmake(['clean'])
453        self._newestOutput = None
454
455# Backwards compatibility for mx_jvmci:8 API
456def buildvms(args):
457    _runmultimake(args)
458
459def run_vm(args, vm=None, nonZeroIsFatal=True, out=None, err=None, cwd=None, timeout=None, debugLevel=None, vmbuild=None):
460    """run a Java program by executing the java executable in a JVMCI JDK"""
461    jdkTag = mx.get_jdk_option().tag
462    if jdkTag and jdkTag != _JVMCI_JDK_TAG:
463        mx.abort('The "--jdk" option must have the tag "' + _JVMCI_JDK_TAG + '" when running a command requiring a JVMCI VM')
464    jdk = get_jvmci_jdk(debugLevel=debugLevel or _translateLegacyDebugLevel(vmbuild))
465    return jdk.run_java(args, nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd, timeout=timeout)
466
467def _unittest_vm_launcher(vmArgs, mainClass, mainClassArgs):
468    run_vm(vmArgs + [mainClass] + mainClassArgs)
469
470mx_unittest.set_vm_launcher('JVMCI VM launcher', _unittest_vm_launcher)
471
472def _jvmci_gate_runner(args, tasks):
473    # Build release server VM now so we can run the unit tests
474    with Task('BuildHotSpotJVMCIHosted: release', tasks) as t:
475        if t: _runmultimake(['--jdk-jvm-variants', 'server', '--jdk-debug-levels', 'release'])
476
477    # Run unit tests in hosted mode
478    with VM(jvmVariant='server', debugLevel='release', jvmciMode='hosted'):
479        with Task('JVMCI UnitTests: hosted-release', tasks) as t:
480            if t: unittest(['--suite', 'jvmci', '--enable-timing', '--verbose', '--fail-fast'])
481
482    # Build the other VM flavors
483    with Task('BuildHotSpotJVMCIOthers: fastdebug', tasks) as t:
484        if t: _runmultimake(['--jdk-jvm-variants', 'server', '--jdk-debug-levels', 'fastdebug'])
485
486    with Task('CleanAndBuildIdealGraphVisualizer', tasks, disableJacoco=True) as t:
487        if t and platform.processor() != 'sparc':
488            buildxml = mx._cygpathU2W(join(_suite.dir, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'build.xml'))
489            mx.run(['ant', '-f', buildxml, '-q', 'clean', 'build'], env=_igvBuildEnv())
490
491mx_gate.add_gate_runner(_suite, _jvmci_gate_runner)
492mx_gate.add_gate_argument('-g', '--only-build-jvmci', action='store_false', dest='buildNonJVMCI', help='only build the JVMCI VM')
493
494def _igvJdk():
495    v8u20 = mx.VersionSpec("1.8.0_20")
496    v8u40 = mx.VersionSpec("1.8.0_40")
497    v8 = mx.VersionSpec("1.8")
498    def _igvJdkVersionCheck(version):
499        return version >= v8 and (version < v8u20 or version >= v8u40)
500    return mx.get_jdk(_igvJdkVersionCheck, versionDescription='>= 1.8 and < 1.8.0u20 or >= 1.8.0u40', purpose="building & running IGV").home
501
502def _igvBuildEnv():
503        # When the http_proxy environment variable is set, convert it to the proxy settings that ant needs
504    env = dict(os.environ)
505    proxy = os.environ.get('http_proxy')
506    if not (proxy is None) and len(proxy) > 0:
507        if '://' in proxy:
508            # Remove the http:// prefix (or any other protocol prefix)
509            proxy = proxy.split('://', 1)[1]
510        # Separate proxy server name and port number
511        proxyName, proxyPort = proxy.split(':', 1)
512        proxyEnv = '-DproxyHost="' + proxyName + '" -DproxyPort=' + proxyPort
513        env['ANT_OPTS'] = proxyEnv
514
515    env['JAVA_HOME'] = _igvJdk()
516    return env
517
518def igv(args):
519    """run the Ideal Graph Visualizer"""
520    logFile = '.ideal_graph_visualizer.log'
521    with open(join(_suite.dir, logFile), 'w') as fp:
522        mx.logv('[Ideal Graph Visualizer log is in ' + fp.name + ']')
523        nbplatform = join(_suite.dir, 'src', 'share', 'tools', 'IdealGraphVisualizer', 'nbplatform')
524
525        # Remove NetBeans platform if it is earlier than the current supported version
526        if exists(nbplatform):
527            updateTrackingFile = join(nbplatform, 'platform', 'update_tracking', 'org-netbeans-core.xml')
528            if not exists(updateTrackingFile):
529                mx.log('Could not find \'' + updateTrackingFile + '\', removing NetBeans platform')
530                shutil.rmtree(nbplatform)
531            else:
532                dom = xml.dom.minidom.parse(updateTrackingFile)
533                currentVersion = mx.VersionSpec(dom.getElementsByTagName('module_version')[0].getAttribute('specification_version'))
534                supportedVersion = mx.VersionSpec('3.43.1')
535                if currentVersion < supportedVersion:
536                    mx.log('Replacing NetBeans platform version ' + str(currentVersion) + ' with version ' + str(supportedVersion))
537                    shutil.rmtree(nbplatform)
538                elif supportedVersion < currentVersion:
539                    mx.log('Supported NetBeans version in igv command should be updated to ' + str(currentVersion))
540
541        if not exists(nbplatform):
542            mx.logv('[This execution may take a while as the NetBeans platform needs to be downloaded]')
543
544        env = _igvBuildEnv()
545        # make the jar for Batik 1.7 available.
546        env['IGV_BATIK_JAR'] = mx.library('BATIK').get_path(True)
547        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):
548            mx.abort("IGV ant build & launch failed. Check '" + logFile + "'. You can also try to delete 'src/share/tools/IdealGraphVisualizer/nbplatform'.")
549
550def c1visualizer(args):
551    """run the Cl Compiler Visualizer"""
552    libpath = join(_suite.dir, 'lib')
553    if mx.get_os() == 'windows':
554        executable = join(libpath, 'c1visualizer', 'bin', 'c1visualizer.exe')
555    else:
556        executable = join(libpath, 'c1visualizer', 'bin', 'c1visualizer')
557
558    # Check whether the current C1Visualizer installation is the up-to-date
559    if exists(executable) and not exists(mx.library('C1VISUALIZER_DIST').get_path(resolve=False)):
560        mx.log('Updating C1Visualizer')
561        shutil.rmtree(join(libpath, 'c1visualizer'))
562
563    archive = mx.library('C1VISUALIZER_DIST').get_path(resolve=True)
564
565    if not exists(executable):
566        zf = zipfile.ZipFile(archive, 'r')
567        zf.extractall(libpath)
568
569    if not exists(executable):
570        mx.abort('C1Visualizer binary does not exist: ' + executable)
571
572    if mx.get_os() != 'windows':
573        # Make sure that execution is allowed. The zip file does not always specfiy that correctly
574        os.chmod(executable, 0777)
575
576    mx.run([executable])
577
578def hsdis(args, copyToDir=None):
579    """download the hsdis library
580
581    This is needed to support HotSpot's assembly dumping features.
582    By default it downloads the Intel syntax version, use the 'att' argument to install AT&T syntax."""
583    flavor = 'intel'
584    if 'att' in args:
585        flavor = 'att'
586    if mx.get_arch() == "sparcv9":
587        flavor = "sparcv9"
588    lib = mx.add_lib_suffix('hsdis-' + mx.get_arch())
589    path = join(_suite.dir, 'lib', lib)
590
591    sha1s = {
592        'att/hsdis-amd64.dll' : 'bcbd535a9568b5075ab41e96205e26a2bac64f72',
593        'att/hsdis-amd64.so' : '58919ba085d4ef7a513f25bae75e7e54ee73c049',
594        'intel/hsdis-amd64.dll' : '6a388372cdd5fe905c1a26ced614334e405d1f30',
595        'intel/hsdis-amd64.so' : '844ed9ffed64fe9599638f29a8450c50140e3192',
596        'intel/hsdis-amd64.dylib' : 'fdb13ef0d7d23d93dacaae9c98837bea0d4fc5a2',
597        'sparcv9/hsdis-sparcv9.so': '970640a9af0bd63641f9063c11275b371a59ee60',
598    }
599
600    flavoredLib = flavor + "/" + lib
601    if flavoredLib not in sha1s:
602        mx.logv("hsdis not supported on this plattform or architecture")
603        return
604
605    if not exists(path):
606        sha1 = sha1s[flavoredLib]
607        sha1path = path + '.sha1'
608        mx.download_file_with_sha1('hsdis', path, ['https://lafo.ssw.uni-linz.ac.at/pub/hsdis/' + flavoredLib], sha1, sha1path, True, True, sources=False)
609    if copyToDir is not None and exists(copyToDir):
610        shutil.copy(path, copyToDir)
611
612def hcfdis(args):
613    """disassemble HexCodeFiles embedded in text files
614
615    Run a tool over the input files to convert all embedded HexCodeFiles
616    to a disassembled format."""
617
618    parser = ArgumentParser(prog='mx hcfdis')
619    parser.add_argument('-m', '--map', help='address to symbol map applied to disassembler output')
620    parser.add_argument('files', nargs=REMAINDER, metavar='files...')
621
622    args = parser.parse_args(args)
623
624    path = mx.library('HCFDIS').get_path(resolve=True)
625    mx.run_java(['-cp', path, 'com.oracle.max.hcfdis.HexCodeFileDis'] + args.files)
626
627    if args.map is not None:
628        addressRE = re.compile(r'0[xX]([A-Fa-f0-9]+)')
629        with open(args.map) as fp:
630            lines = fp.read().splitlines()
631        symbols = dict()
632        for l in lines:
633            addressAndSymbol = l.split(' ', 1)
634            if len(addressAndSymbol) == 2:
635                address, symbol = addressAndSymbol
636                if address.startswith('0x'):
637                    address = long(address, 16)
638                    symbols[address] = symbol
639        for f in args.files:
640            with open(f) as fp:
641                lines = fp.read().splitlines()
642            updated = False
643            for i in range(0, len(lines)):
644                l = lines[i]
645                for m in addressRE.finditer(l):
646                    sval = m.group(0)
647                    val = long(sval, 16)
648                    sym = symbols.get(val)
649                    if sym:
650                        l = l.replace(sval, sym)
651                        updated = True
652                        lines[i] = l
653            if updated:
654                mx.log('updating ' + f)
655                with open('new_' + f, "w") as fp:
656                    for l in lines:
657                        print >> fp, l
658
659def jol(args):
660    """Java Object Layout"""
661    joljar = mx.library('JOL_INTERNALS').get_path(resolve=True)
662    candidates = mx.findclass(args, logToConsole=False, matcher=lambda s, classname: s == classname or classname.endswith('.' + s) or classname.endswith('$' + s))
663
664    if len(candidates) > 0:
665        candidates = mx.select_items(sorted(candidates))
666    else:
667        # mx.findclass can be mistaken, don't give up yet
668        candidates = args
669
670    run_vm(['-javaagent:' + joljar, '-cp', os.pathsep.join([mx.classpath(), joljar]), "org.openjdk.jol.MainObjectInternals"] + candidates)
671
672class JVMCIArchiveParticipant:
673    def __init__(self, dist):
674        self.dist = dist
675
676    def __opened__(self, arc, srcArc, services):
677        self.services = services
678        self.jvmciServices = services
679        self.arc = arc
680
681    def __add__(self, arcname, contents):
682        return False
683
684    def __addsrc__(self, arcname, contents):
685        return False
686
687    def __closing__(self):
688        pass
689
690def _get_openjdk_os():
691    # See: common/autoconf/platform.m4
692    os = mx.get_os()
693    if 'darwin' in os:
694        os = 'macosx'
695    elif 'linux' in os:
696        os = 'linux'
697    elif 'solaris' in os:
698        os = 'solaris'
699    elif 'cygwin' in os or 'mingw' in os:
700        os = 'windows'
701    return os
702
703def _get_openjdk_cpu():
704    cpu = mx.get_arch()
705    if cpu == 'amd64':
706        cpu = 'x86_64'
707    elif cpu == 'sparcv9':
708        cpu = 'sparcv9'
709    return cpu
710
711def _get_openjdk_os_cpu():
712    return _get_openjdk_os() + '-' + _get_openjdk_cpu()
713
714def _get_jdk_build_dir(debugLevel=None):
715    """
716    Gets the directory into which the JDK is built. This directory contains
717    the exploded JDK under jdk/ and the JDK image under images/jdk/.
718    """
719    if debugLevel is None:
720        debugLevel = _vm.debugLevel
721    name = '{}-{}-{}-{}'.format(_get_openjdk_os_cpu(), 'normal', _vm.jvmVariant, debugLevel)
722    return join(dirname(_suite.dir), 'build', name)
723
724_jvmci_bootclasspath_prepends = []
725
726def _get_hotspot_build_dir(jvmVariant=None, debugLevel=None):
727    """
728    Gets the directory in which a particular HotSpot configuration is built
729    (e.g., <JDK_REPO_ROOT>/build/macosx-x86_64-normal-server-release/hotspot/bsd_amd64_compiler2)
730    """
731    if jvmVariant is None:
732        jvmVariant = _vm.jvmVariant
733
734    os = mx.get_os()
735    if os == 'darwin':
736        os = 'bsd'
737    arch = mx.get_arch()
738    buildname = {'client': 'compiler1', 'server': 'compiler2'}.get(jvmVariant, jvmVariant)
739
740    name = '{}_{}_{}'.format(os, arch, buildname)
741    return join(_get_jdk_build_dir(debugLevel=debugLevel), 'hotspot', name)
742
743def add_bootclasspath_prepend(dep):
744    assert isinstance(dep, mx.ClasspathDependency)
745    _jvmci_bootclasspath_prepends.append(dep)
746
747class JVMCI9JDKConfig(mx.JDKConfig):
748    def __init__(self, debugLevel):
749        self.debugLevel = debugLevel
750        jdkBuildDir = _get_jdk_build_dir(debugLevel)
751        jdkDir = join(jdkBuildDir, 'images', 'jdk') if mx.get_opts().use_jdk_image else join(jdkBuildDir, 'jdk')
752        mx.JDKConfig.__init__(self, jdkDir, tag=_JVMCI_JDK_TAG)
753
754    def parseVmArgs(self, args, addDefaultArgs=True):
755        args = mx.expand_project_in_args(args, insitu=False)
756        jacocoArgs = mx_gate.get_jacoco_agent_args()
757        if jacocoArgs:
758            args = jacocoArgs + args
759
760        args = ['-Xbootclasspath/p:' + dep.classpath_repr() for dep in _jvmci_bootclasspath_prepends] + args
761
762        jvmciModeArgs = _jvmciModes[_vm.jvmciMode]
763        if jvmciModeArgs:
764            bcpDeps = [jdkDist.dist() for jdkDist in jdkDeployedDists]
765            if bcpDeps:
766                args = ['-Xbootclasspath/p:' + os.pathsep.join([d.classpath_repr() for d in bcpDeps])] + args
767
768        # Set the default JVMCI compiler
769        for jdkDist in reversed(jdkDeployedDists):
770            assert isinstance(jdkDist, JvmciJDKDeployedDist), jdkDist
771            if jdkDist._compilers:
772                jvmciCompiler = jdkDist._compilers[-1]
773                args = ['-Djvmci.compiler=' + jvmciCompiler] + args
774                break
775
776        if '-version' in args:
777            ignoredArgs = args[args.index('-version') + 1:]
778            if  len(ignoredArgs) > 0:
779                mx.log("Warning: The following options will be ignored by the vm because they come after the '-version' argument: " + ' '.join(ignoredArgs))
780        return self.processArgs(args, addDefaultArgs=addDefaultArgs)
781
782    # Overrides JDKConfig
783    def run_java(self, args, vm=None, nonZeroIsFatal=True, out=None, err=None, cwd=None, timeout=None, env=None, addDefaultArgs=True):
784        if vm is None:
785            vm = 'server'
786
787        args = self.parseVmArgs(args, addDefaultArgs=addDefaultArgs)
788
789        jvmciModeArgs = _jvmciModes[_vm.jvmciMode]
790        cmd = [self.java] + ['-' + vm] + jvmciModeArgs + args
791        return mx.run(cmd, nonZeroIsFatal=nonZeroIsFatal, out=out, err=err, cwd=cwd)
792
793"""
794The dict of JVMCI JDKs indexed by debug-level names.
795"""
796_jvmci_jdks = {}
797
798def get_jvmci_jdk(debugLevel=None):
799    """
800    Gets the JVMCI JDK corresponding to 'debugLevel'.
801    """
802    if not debugLevel:
803        debugLevel = _vm.debugLevel
804    jdk = _jvmci_jdks.get(debugLevel)
805    if jdk is None:
806        try:
807            jdk = JVMCI9JDKConfig(debugLevel)
808        except mx.JDKConfigException as e:
809            jdkBuildDir = _get_jdk_build_dir(debugLevel)
810            msg = 'Error with the JDK built into {}:\n{}\nTry (re)building it with: mx --jdk-debug-level={} make'
811            if mx.get_opts().use_jdk_image:
812                msg += ' images'
813            mx.abort(msg.format(jdkBuildDir, e.message, debugLevel))
814        _jvmci_jdks[debugLevel] = jdk
815    return jdk
816
817class JVMCIJDKFactory(mx.JDKFactory):
818    def getJDKConfig(self):
819        jdk = get_jvmci_jdk(_vm.debugLevel)
820        return jdk
821
822    def description(self):
823        return "JVMCI JDK"
824
825mx.update_commands(_suite, {
826    'make': [_runmake, '[args...]', _makehelp],
827    'multimake': [_runmultimake, '[options]'],
828    'c1visualizer' : [c1visualizer, ''],
829    'hsdis': [hsdis, '[att]'],
830    'hcfdis': [hcfdis, ''],
831    'igv' : [igv, ''],
832    'jol' : [jol, ''],
833    'vm': [run_vm, '[-options] class [args...]'],
834})
835
836mx.add_argument('-M', '--jvmci-mode', action='store', choices=sorted(_jvmciModes.viewkeys()), help='the JVM variant type to build/run (default: ' + _vm.jvmciMode + ')')
837mx.add_argument('--jdk-jvm-variant', '--vm', action='store', choices=_jdkJvmVariants + sorted(_legacyVms.viewkeys()), help='the JVM variant type to build/run (default: ' + _vm.jvmVariant + ')')
838mx.add_argument('--jdk-debug-level', '--vmbuild', action='store', choices=_jdkDebugLevels + sorted(_legacyVmbuilds.viewkeys()), help='the JDK debug level to build/run (default: ' + _vm.debugLevel + ')')
839mx.add_argument('-I', '--use-jdk-image', action='store_true', help='build/run JDK image instead of exploded JDK')
840
841def mx_post_parse_cmd_line(opts):
842    mx.addJDKFactory(_JVMCI_JDK_TAG, mx.JavaCompliance('9'), JVMCIJDKFactory())
843    mx.set_java_command_default_jdk_tag(_JVMCI_JDK_TAG)
844
845    jdkTag = mx.get_jdk_option().tag
846
847    jvmVariant = None
848    debugLevel = None
849    jvmciMode = None
850
851    if opts.jdk_jvm_variant is not None:
852        jvmVariant = opts.jdk_jvm_variant
853        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
854            mx.warn('Ignoring "--jdk-jvm-variant" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
855
856    if opts.jdk_debug_level is not None:
857        debugLevel = _translateLegacyDebugLevel(opts.jdk_debug_level)
858        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
859            mx.warn('Ignoring "--jdk-debug-level" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
860
861    if opts.jvmci_mode is not None:
862        jvmciMode = opts.jvmci_mode
863        if jdkTag and jdkTag != _JVMCI_JDK_TAG:
864            mx.warn('Ignoring "--jvmci-mode" option as "--jdk" tag is not "' + _JVMCI_JDK_TAG + '"')
865
866    _vm.update(jvmVariant, debugLevel, jvmciMode)
867
868    for jdkDist in jdkDeployedDists:
869        jdkDist.post_parse_cmd_line()
870