예제 #1
0
def test(args):
    """run some or all of the Maxine tests

    The Maxine sources include a variety of tests that can be run by a
    special launcher. These include JUnit tests, VM micro tests, certain
    benchmark suites and output comparison tests, amongst others.

    Use "mx test -help" to see what other options this command accepts."""
    maxineTesterDir = join(_maxine_home, 'maxine-tester')
    if isdir(maxineTesterDir):
        for root, _, files in os.walk(maxineTesterDir):
            for name in files:
                if name.rsplit(', ', 1) in ['stdout', 'stderr', 'passed', 'failed', 'command']:
                    os.remove(join(root, name))
    else:
        os.mkdir(maxineTesterDir)

    class Tee:
        def __init__(self, f):
            self.f = f
        def eat(self, line):
            mx.log(line.rstrip())
            self.f.write(line)

    console = join(maxineTesterDir, 'console')
    with open(console, 'w', 0) as f:
        tee = Tee(f)
        java = mx.java()
        mx.run_java(['-cp', sanitized_classpath(), 'test.com.sun.max.vm.MaxineTester', '-output-dir=maxine-tester',
                      '-graal-jar=' + mx.distribution('GRAAL').path,
                      '-refvm=' + java.java, '-refvm-args=' + ' '.join(java.java_args)] + args, out=tee.eat, err=subprocess.STDOUT)
예제 #2
0
def _configs():
    class Configs:
        def __init__(self):
            self.configs = dict()

        def eat(self, line):
            (k, v) = line.split('#')
            self.configs[k] = v.rstrip()
    c = Configs()
    mx.run([mx.java().java, '-client', '-Xmx40m', '-Xms40m', '-XX:NewSize=30m', '-cp', mx.classpath(resolve=False), 'test.com.sun.max.vm.MaxineTesterConfiguration'], out=c.eat)
    return c.configs
예제 #3
0
def makejdk(args):
    """create a JDK directory based on the Maxine VM

    Create a JDK directory by replicating the file structure of $JAVA_HOME
    and replacing the 'java' executable with the Maxine VM
    executable. This produces a Maxine VM based JDK for applications
    (such as NetBeans) which expect a certain directory structure
    and executable names in a JDK installation."""

    if mx.os == 'darwin':
        mx.log('mx makejdk is not supported on Darwin')
        mx.abort(1)

    if len(args) == 0:
        maxjdk = join(_maxine_home, 'maxjdk')
    else:
        maxjdk = args[0]
        if maxjdk[0] != '/':
            maxjdk = join(os.getcwd(), maxjdk)

    if exists(maxjdk):
        mx.log('The destination directory already exists -- it will be deleted')
        shutil.rmtree(maxjdk)

    jdk = mx.java().jdk
    if not isdir(jdk):
        mx.log(jdk + " does not exist or is not a directory")
        mx.abort(1)

    mx.log('Replicating ' + jdk + ' in ' + maxjdk + '...')
    shutil.copytree(jdk, maxjdk, symlinks=True)

    jreExists = exists(join(maxjdk, 'jre'))

    for f in os.listdir(_vmdir):
        fpath = join(_vmdir, f)
        if isfile(fpath):
            shutil.copy(fpath, join(maxjdk, 'bin'))
            if jreExists:
                shutil.copy(fpath, join(maxjdk, 'jre', 'bin'))

    os.unlink(join(maxjdk, 'bin', 'java'))
    if jreExists:
        os.unlink(join(maxjdk, 'jre', 'bin', 'java'))
    if (mx.os == 'windows'):
        shutil.copy(join(maxjdk, 'bin', 'maxvm'), join(maxjdk, 'bin', 'java'))
        shutil.copy(join(maxjdk, 'jre', 'bin', 'maxvm'), join(maxjdk, 'jre', 'bin', 'java'))
    else:
        os.symlink(join(maxjdk, 'bin', 'maxvm'), join(maxjdk, 'bin', 'java'))
        if jreExists:
            os.symlink(join(maxjdk, 'jre', 'bin', 'maxvm'), join(maxjdk, 'jre', 'bin', 'java'))

    mx.log('Created Maxine based JDK in ' + maxjdk)
예제 #4
0
def makejdk(args):
    """create a JDK directory based on the Maxine VM

    Create a JDK directory by replicating the file structure of $JAVA_HOME
    and replacing the 'java' executable with the Maxine VM
    executable. This produces a Maxine VM based JDK for applications
    (such as NetBeans) which expect a certain directory structure
    and executable names in a JDK installation."""

    if mx.os == 'darwin':
        mx.log('mx makejdk is not supported on Darwin')
        mx.abort(1)

    if len(args) == 0:
        maxjdk = join(_maxine_home, 'maxjdk')
    else:
        maxjdk = args[0]
        if maxjdk[0] != '/':
            maxjdk = join(os.getcwd(), maxjdk)

    if exists(maxjdk):
        mx.log('The destination directory already exists -- it will be deleted')
        shutil.rmtree(maxjdk)

    jdk = mx.java().jdk
    if not isdir(jdk):
        mx.log(jdk + " does not exist or is not a directory")
        mx.abort(1)

    mx.log('Replicating ' + jdk + ' in ' + maxjdk + '...')
    shutil.copytree(jdk, maxjdk, symlinks=True)

    jreExists = exists(join(maxjdk, 'jre'))

    for f in os.listdir(_vmdir):
        fpath = join(_vmdir, f)
        if isfile(fpath):
            shutil.copy(fpath, join(maxjdk, 'bin'))
            if jreExists:
                shutil.copy(fpath, join(maxjdk, 'jre', 'bin'))

    os.unlink(join(maxjdk, 'bin', 'java'))
    if jreExists:
        os.unlink(join(maxjdk, 'jre', 'bin', 'java'))
    if (mx.os == 'windows'):
        shutil.copy(join(maxjdk, 'bin', 'maxvm'), join(maxjdk, 'bin', 'java'))
        shutil.copy(join(maxjdk, 'jre', 'bin', 'maxvm'), join(maxjdk, 'jre', 'bin', 'java'))
    else:
        os.symlink(join(maxjdk, 'bin', 'maxvm'), join(maxjdk, 'bin', 'java'))
        if jreExists:
            os.symlink(join(maxjdk, 'jre', 'bin', 'maxvm'), join(maxjdk, 'jre', 'bin', 'java'))

    mx.log('Created Maxine based JDK in ' + maxjdk)
예제 #5
0
def inspectoragent(args):
    """launch the Inspector agent

    Launch the Inspector agent.

    The agent listens on a given port for an incoming connection from
    a remote Inspector process."""

    cmd = mx.java().format_cmd(['-cp', mx.classpath(), 'com.sun.max.tele.channel.agent.InspectorAgent'] + args)
    if mx.get_os() == 'darwin':
        # The -E option propagates the environment variables into the sudo process
        mx.run(['sudo', '-E', '-p', 'Debugging is a privileged operation on Mac OS X.\nPlease enter your "sudo" password:'] + cmd)
    else:
        mx.run(cmd)
예제 #6
0
def inspectoragent(args):
    """launch the Inspector agent

    Launch the Inspector agent.

    The agent listens on a given port for an incoming connection from
    a remote Inspector process."""

    cmd = mx.java().format_cmd(['-cp', mx.classpath(), 'com.sun.max.tele.channel.agent.InspectorAgent'] + args)
    if mx.get_os() == 'darwin':
        # The -E option propagates the environment variables into the sudo process
        mx.run(['sudo', '-E', '-p', 'Debugging is a privileged operation on Mac OS X.\nPlease enter your "sudo" password:'] + cmd)
    else:
        mx.run(cmd)
예제 #7
0
def _configs():
    class Configs:
        def __init__(self):
            self.configs = dict()

        def eat(self, line):
            (k, v) = line.split('#')
            self.configs[k] = v.rstrip()

    c = Configs()
    mx.run([
        mx.java().java, '-client', '-Xmx40m', '-Xms40m', '-XX:NewSize=30m',
        '-cp',
        mx.classpath(resolve=False),
        'test.com.sun.max.vm.MaxineTesterConfiguration'
    ],
           out=c.eat)
    return c.configs
예제 #8
0
def vm(args):
    """launch the Maxine VM

    Run the Maxine VM with the given options and arguments.
    A class path component with a '@' prefix is expanded to be the
    class path of the project named after the '@'.
    The expansion of the MAXVM_OPTIONS environment variable is inserted
    before any other VM options specified on the command line.

    Use "mx vm -help" to see what other options this command accepts."""

    mx.expand_project_in_args(args)
    maxvmOptions = os.getenv('MAXVM_OPTIONS', '').split()

    debug_port = mx.java().debug_port
    if debug_port is not None:
        maxvmOptions += ['-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(debug_port)]

    mx.run([join(_vmdir, 'maxvm')] + maxvmOptions + args)
예제 #9
0
def test(args):
    """run some or all of the Maxine tests


    The Maxine sources include a variety of tests that can be run by a
    special launcher. These include JUnit tests, VM micro tests, certain
    benchmark suites and output comparison tests, amongst others.

    Use "mx test -help" to see what other options this command accepts."""
    maxineTesterDir = join(_maxine_home, 'maxine-tester')
    if isdir(maxineTesterDir):
        for root, _, files in os.walk(maxineTesterDir):
            for name in files:
                if name.rsplit(', ', 1) in [
                        'stdout', 'stderr', 'passed', 'failed', 'command'
                ]:
                    os.remove(join(root, name))
    else:
        os.mkdir(maxineTesterDir)

    class Tee:
        def __init__(self, f):
            self.f = f

        def eat(self, line):
            mx.log(line.rstrip())
            self.f.write(line)

    console = join(maxineTesterDir, 'console')
    with open(console, 'w', 0) as f:
        tee = Tee(f)
        java = mx.java()
        mx.run_java([
            '-cp',
            sanitized_classpath(), 'test.com.sun.max.vm.MaxineTester',
            '-output-dir=maxine-tester',
            '-graal-jar=' + mx.distribution('GRAAL').path,
            '-refvm=' + java.java, '-refvm-args=' + ' '.join(java.java_args)
        ] + args,
                    out=tee.eat,
                    err=subprocess.STDOUT)
예제 #10
0
def findbugs(args, fbArgs=None, suite=None, projects=None):
    """run FindBugs against non-test Java projects"""
    findBugsHome = mx.get_env('FINDBUGS_HOME', None)
    if suite is None:
        suite = mx._primary_suite
    if findBugsHome:
        findbugsJar = join(findBugsHome, 'lib', 'findbugs.jar')
    else:
        findbugsLib = join(mx._mx_suite.dir, 'lib', 'findbugs-3.0.0')
        if not exists(findbugsLib):
            tmp = tempfile.mkdtemp(prefix='findbugs-download-tmp', dir=mx._mx_suite.dir)
            try:
                findbugsDist = mx.library('FINDBUGS_DIST').get_path(resolve=True)
                with zipfile.ZipFile(findbugsDist) as zf:
                    candidates = [e for e in zf.namelist() if e.endswith('/lib/findbugs.jar')]
                    assert len(candidates) == 1, candidates
                    libDirInZip = os.path.dirname(candidates[0])
                    zf.extractall(tmp)
                shutil.copytree(join(tmp, libDirInZip), findbugsLib)
            finally:
                shutil.rmtree(tmp)
        findbugsJar = join(findbugsLib, 'findbugs.jar')
    assert exists(findbugsJar)
    nonTestProjects = [p for p in mx.projects() if not p.name.endswith('.test') and not p.name.endswith('.jtt')]
    outputDirs = map(mx._cygpathU2W, [p.output_dir() for p in nonTestProjects])
    javaCompliance = max([p.javaCompliance for p in nonTestProjects])
    findbugsResults = join(suite.dir, 'findbugs.results')

    if fbArgs is None:
        fbArgs = defaultFindbugsArgs()
    cmd = ['-jar', mx._cygpathU2W(findbugsJar)] + fbArgs
    cmd = cmd + ['-auxclasspath', mx._separatedCygpathU2W(mx.classpath([p.name for p in nonTestProjects])), '-output', mx._cygpathU2W(findbugsResults), '-exitcode'] + args + outputDirs
    exitcode = mx.run_java(cmd, nonZeroIsFatal=False, javaConfig=mx.java(javaCompliance))
    if exitcode != 0:
        with open(findbugsResults) as fp:
            mx.log(fp.read())
    os.unlink(findbugsResults)
    return exitcode
예제 #11
0
def vm(args):
    """launch the Maxine VM

    Run the Maxine VM with the given options and arguments.
    A class path component with a '@' prefix is expanded to be the
    class path of the project named after the '@'.
    The expansion of the MAXVM_OPTIONS environment variable is inserted
    before any other VM options specified on the command line.

    Use "mx vm -help" to see what other options this command accepts."""

    cwdArgs = check_cwd_change(args)
    cwd = cwdArgs[0]
    vmArgs = cwdArgs[1]
    
    mx.expand_project_in_args(vmArgs)
    maxvmOptions = os.getenv('MAXVM_OPTIONS', '').split()

    debug_port = mx.java().debug_port
    if debug_port is not None:
        maxvmOptions += ['-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=' + str(debug_port)]

    mx.run([join(_vmdir, 'maxvm')] + maxvmOptions + vmArgs, cwd=cwd)
예제 #12
0
def _run_tests(args, harness, vmLauncher, annotations, testfile, blacklist, whitelist, regex):


    vmArgs, tests = mx.extract_VM_args(args)
    for t in tests:
        if t.startswith('-'):
            mx.abort('VM option ' + t + ' must precede ' + tests[0])

    candidates = {}
    for p in mx.projects_opt_limit_to_suites():
        if mx.java().javaCompliance < p.javaCompliance:
            continue
        for c in _find_classes_with_annotations(p, None, annotations).keys():
            candidates[c] = p

    classes = []
    if len(tests) == 0:
        classes = candidates.keys()
        projectsCp = mx.classpath([pcp.name for pcp in mx.projects_opt_limit_to_suites() if pcp.javaCompliance <= mx.java().javaCompliance])
    else:
        projs = set()
        found = False
        if len(tests) == 1 and '#' in tests[0]:
            words = tests[0].split('#')
            if len(words) != 2:
                mx.abort("Method specification is class#method: " + tests[0])
            t, method = words

            for c, p in candidates.iteritems():
                # prefer exact matches first
                if t == c:
                    found = True
                    classes.append(c)
                    projs.add(p.name)
            if not found:
                for c, p in candidates.iteritems():
                    if t in c:
                        found = True
                        classes.append(c)
                        projs.add(p.name)
            if not found:
                mx.log('warning: no tests matched by substring "' + t)
            elif len(classes) != 1:
                mx.abort('More than one test matches substring {0} {1}'.format(t, classes))

            classes = [c + "#" + method for c in classes]
        else:
            for t in tests:
                if '#' in t:
                    mx.abort('Method specifications can only be used in a single test: ' + t)
                for c, p in candidates.iteritems():
                    if t in c:
                        found = True
                        classes.append(c)
                        projs.add(p.name)
                if not found:
                    mx.log('warning: no tests matched by substring "' + t)
        projectsCp = mx.classpath(projs)

    if blacklist:
        classes = [c for c in classes if not any((glob.match(c) for glob in blacklist))]

    if whitelist:
        classes = [c for c in classes if any((glob.match(c) for glob in whitelist))]

    if regex:
        classes = [c for c in classes if re.search(regex, c)]

    if len(classes) != 0:
        f_testfile = open(testfile, 'w')
        for c in classes:
            f_testfile.write(c + '\n')
        f_testfile.close()
        harness(projectsCp, vmLauncher, vmArgs)
예제 #13
0
def inspect(args):
    """launch a given program under the Inspector

    Run Maxine under the Inspector. The arguments accepted by this command
    are those accepted by the 'mx vm' command plus the Inspector specific
    options. To debug a program in the Inspector, simply replace 'vm' on the
    command line that launches the program with 'inspect'.

    Use "mx inspect --help" to see what the Inspector options are. These options
    must be specified with a '--' prefix so that they can be distinguished from
    the VM options.

    The inspect command also accepts the same system property related options
    as the 'image' command except that a '--' prefix must be used (e.g.
    '--os Darwin --bits 32'). Use "mx help image" for more detail.

    Use "mx vm -help" to see what the VM options are."""

    saveClassDir = join(_vmdir, 'inspected_classes')
    maxvmOptions = os.getenv('MAXVM_OPTIONS', '').split()
    vmArgs = ['-XX:SaveClassDir=' + saveClassDir, '-XX:+TrapOnError'] + maxvmOptions
    insArgs = ['-vmdir=' + _vmdir]
    if not isdir(saveClassDir):
        os.makedirs(saveClassDir)
    sysProps = []
    insCP = []

    i = 0
    remote = False
    while i < len(args):
        arg = args[i]
        if arg.startswith('-XX:LogFile='):
            logFile = arg.split('=', 1)[1]
            vmArgs += [arg]
            os.environ['TELE_LOG_FILE'] = 'tele-' + logFile
        elif arg in ['-cp', '-classpath']:
            vmArgs += [arg, args[i + 1]]
            insCP += [mx.expand_project_in_class_path_arg(args[i + 1])]
            i += 1
        elif arg == '-jar':
            vmArgs += ['-jar', args[i + 1]]
            insCP += [args[i + 1]]
            i += 1
        elif arg == '--remote':
            remote = True
        elif arg in ['--platform', '--cpu', '--isa', '--os', '--endianness', '--bits', '--page', '--nsig']:
            name = arg.lstrip('-')
            i += 1
            value = args[i]
            sysProps += ['-Dmax.' + name + '=' + value]
        elif arg.startswith('--cp='):
            insCP += [arg[len('--cp='):]]
        elif arg.startswith('--'):
            # chomp leading '-'
            insArgs += [arg[1:]]
        elif arg.startswith('-XX:SaveClassDir='):
            vmArgs += [arg]
            saveClassDir = arg.split('=', 1)[1]
            if not isdir(saveClassDir):
                os.makedirs(saveClassDir)
        elif arg.startswith('-'):
            vmArgs += [arg]
        else:
            # This is the main class argument; copy it and any following
            # arguments to the VM verbatim
            vmArgs += args[i:]
            break
        i += 1

    insCP += [saveClassDir]
    insCP = pathsep.join(insCP)
    insArgs += ['-cp=' + insCP]

    mx.expand_project_in_args(vmArgs)

    cmd = mx.java().format_cmd(sysProps + ['-cp', mx.classpath() + pathsep + insCP, 'com.sun.max.ins.MaxineInspector'] +
                              insArgs + ['-a=' + ' '.join(vmArgs)])

    if mx.get_os() == 'darwin' and not remote:
        # The -E option propagates the environment variables into the sudo process
        mx.run(['sudo', '-E', '-p', 'Debugging is a privileged operation on Mac OS X.\nPlease enter your "sudo" password:'] + cmd)
    else:
        mx.run(cmd)
예제 #14
0
def inspect(args):
    """launch a given program under the Inspector

    Run Maxine under the Inspector. The arguments accepted by this command
    are those accepted by the 'mx vm' command plus the Inspector specific
    options. To debug a program in the Inspector, simply replace 'vm' on the
    command line that launches the program with 'inspect'.

    Use "mx inspect --help" to see what the Inspector options are. These options
    must be specified with a '--' prefix so that they can be distinguished from
    the VM options.

    The inspect command also accepts the same system property related options
    as the 'image' command except that a '--' prefix must be used (e.g.
    '--os Darwin --bits 32'). Use "mx help image" for more detail.

    Use "mx vm -help" to see what the VM options are."""

    saveClassDir = join(_vmdir, 'inspected_classes')
    maxvmOptions = os.getenv('MAXVM_OPTIONS', '').split()
    vmArgs = ['-XX:SaveClassDir=' + saveClassDir, '-XX:+TrapOnError'] + maxvmOptions
    insArgs = ['-vmdir=' + _vmdir]
    if not isdir(saveClassDir):
        os.makedirs(saveClassDir)
    sysProps = []
    sysProps += ['-Xbootclasspath/a:' + mx.distribution('GRAAL').path]
    insCP = []

    cwdArgs = check_cwd_change(args)
    cwd = cwdArgs[0]
    args = cwdArgs[1]

    i = 0
    remote = False
    while i < len(args):
        arg = args[i]
        if arg.startswith('-XX:LogFile='):
            logFile = arg.split('=', 1)[1]
            vmArgs += [arg]
            os.environ['TELE_LOG_FILE'] = 'tele-' + logFile
        elif arg in ['-cp', '-classpath']:
            vmArgs += [arg, args[i + 1]]
            insCP += [mx.expand_project_in_class_path_arg(args[i + 1])]
            i += 1
        elif arg == '-jar':
            vmArgs += ['-jar', args[i + 1]]
            insCP += [args[i + 1]]
            i += 1
        elif arg == '--remote':
            remote = True
        elif arg in ['--platform', '--cpu', '--isa', '--os', '--endianness', '--bits', '--page', '--nsig']:
            name = arg.lstrip('-')
            i += 1
            value = args[i]
            sysProps += ['-Dmax.' + name + '=' + value]
        elif arg.startswith('--cp='):
            insCP += [arg[len('--cp='):]]
        elif arg.startswith('--'):
            # chomp leading '-'
            insArgs += [arg[1:]]
        elif arg.startswith('-XX:SaveClassDir='):
            vmArgs += [arg]
            saveClassDir = arg.split('=', 1)[1]
            if not isdir(saveClassDir):
                os.makedirs(saveClassDir)
        elif arg.startswith('-'):
            vmArgs += [arg]
        else:
            # This is the main class argument; copy it and any following
            # arguments to the VM verbatim
            vmArgs += args[i:]
            break
        i += 1

    insCP += [saveClassDir]
    insCP = pathsep.join(insCP)
    insArgs += ['-cp=' + insCP]

    mx.expand_project_in_args(vmArgs)

    cmd = mx.java().format_cmd(sysProps + ['-cp', sanitized_classpath() + pathsep + insCP, 'com.sun.max.ins.MaxineInspector'] +
                              insArgs + ['-a=' + ' '.join(vmArgs)])

    if mx.get_os() == 'darwin' and not remote:
        # The -E option propagates the environment variables into the sudo process
        mx.run(['sudo', '-E', '-p', 'Debugging is a privileged operation on Mac OS X.\nPlease enter your "sudo" password:'] + cmd, cwd=cwd)
    else:
        mx.run(cmd, cwd=cwd)
예제 #15
0
def do_build_makefile(mf, selectedDists):
    java = mx.java()
    bootClassPath = java.bootclasspath()
    bootClassPath = bootClassPath.replace(os.path.realpath(java.jdk), "$(ABS_BOOTDIR)")
    jdkBootClassPathVariableName = "JDK_BOOTCLASSPATH"

    mf.add_definition("""# This Makefile is generated automatically, do not edit

TARGET=.
# Bootstrap JDK to be used (for javac and jar)
ABS_BOOTDIR=

JAVAC=$(ABS_BOOTDIR)/bin/javac -g -target """ + str(java.javaCompliance) + """
JAR=$(ABS_BOOTDIR)/bin/jar

HS_COMMON_SRC=.

# Directories, where the generated property-files reside within the JAR files
PROVIDERS_INF=/META-INF/jvmci.providers
SERVICES_INF=/META-INF/jvmci.services
OPTIONS_INF=/META-INF/jvmci.options

JARS = $(foreach dist,$(DISTRIBUTIONS),$($(dist)_JAR))

ifeq ($(ABS_BOOTDIR),)
    $(error Variable ABS_BOOTDIR must be set to a JDK installation.)
endif
ifeq ($(MAKE_VERBOSE),)
    QUIETLY=@
endif

# Required to construct a whitespace for use with subst
space :=
space +=

# Takes the provider files created by ServiceProviderProcessor (the processor
# for the @ServiceProvider annotation) and merges them into a single file.
# Arguments:
#  1: directory with contents of the JAR file
define process_providers
    $(eval providers := $(1)/$(PROVIDERS_INF))
    $(eval services := $(1)/$(SERVICES_INF))
    $(QUIETLY) test -d $(services) || mkdir -p $(services)
    $(QUIETLY) test ! -d $(providers) || (cd $(providers) && for i in $$(ls); do c=$$(cat $$i); echo $$i >> $(abspath $(services))/$$c; rm $$i; done)

    @# Since all projects are built together with one javac call we cannot determine
    @# which project contains HotSpotVMConfig.inline.hpp so we hardcode it.
    $(eval vmconfig := $(1)/hotspot/HotSpotVMConfig.inline.hpp)
    $(eval vmconfigDest := $(HS_COMMON_SRC)/../jvmci/jdk.internal.jvmci.hotspot/src_gen/hotspot)
    $(QUIETLY) test ! -f $(vmconfig) || (mkdir -p $(vmconfigDest) && cp $(vmconfig) $(vmconfigDest))
endef

# Reads the files in jvmci.options/ created by OptionProcessor (the processor for the @Option annotation)
# and appends to services/jdk.internal.jvmci.options.Options entries for the providers
# also created by the same processor.
# Arguments:
#  1: directory with contents of the JAR file
define process_options
    $(eval options := $(1)/$(OPTIONS_INF))
    $(eval services := $(1)/META-INF/services)
    $(QUIETLY) test -d $(services) || mkdir -p $(services)
    $(QUIETLY) test ! -d $(options) || (cd $(options) && for i in $$(ls); do echo $${i}_Options >> $(abspath $(services))/jdk.internal.jvmci.options.Options; done)
endef

# Extracts META-INF/jvmci.services and META-INF/jvmci.options of a JAR file into a given directory
# Arguments:
#  1: JAR file to extract
#  2: target directory (which already exists)
define extract
    $(eval TMP := $(shell mktemp -d $(TARGET)/tmp_XXXXX))
    $(QUIETLY) cd $(TMP) && $(JAR) xf $(abspath $(1)) && \\
        ((test ! -d .$(SERVICES_INF) || cp -r .$(SERVICES_INF) $(abspath $(2))) && \\
         (test ! -d .$(OPTIONS_INF) || cp -r .$(OPTIONS_INF) $(abspath $(2))));
    $(QUIETLY) rm -r $(TMP);
    $(QUIETLY) cp $(1) $(2)
endef

# Calls $(JAVAC) with the boot class path $(JDK_BOOTCLASSPATH) and sources taken from the automatic variable $^
# Arguments:
#  1: processorpath
#  2: classpath
#  3: resources to copy
#  4: target JAR file
define build_and_jar
    $(info Building $(4))
    $(eval TMP := $(shell mkdir -p $(TARGET) && mktemp -d $(TARGET)/tmp_XXXXX))
    $(QUIETLY) $(JAVAC) -d $(TMP) -processorpath :$(1) -bootclasspath $(JDK_BOOTCLASSPATH) -cp :$(2) $(filter %.java,$^)
    $(QUIETLY) test "$(3)" = "" || cp -r $(3) $(TMP)
    $(QUIETLY) $(call process_options,$(TMP))
    $(QUIETLY) $(call process_providers,$(TMP))
    $(QUIETLY) mkdir -p $(shell dirname $(4))
    $(QUIETLY) $(JAR) -0cf $(4) -C $(TMP) .
    $(QUIETLY) rm -r $(TMP)
endef

# Verifies that make/defs.make contains an appropriate line for each JVMCI service or option
# and that only existing JVMCI services and options are exported.
# Arguments:
#  1: list of service or option files
#  2: variable name for directory of service or option files
define verify_defs_make
    $(eval defs := make/defs.make)
    $(eval uncondPattern := EXPORT_LIST += $$$$($(2))/)
    $(eval condPattern := CONDITIONAL_EXPORT_LIST += $$$$($(2))/)
    $(eval unconditionalExports := $(shell grep '^EXPORT_LIST += $$($2)' make/defs.make | sed 's:.*($(2))/::g'))
    $(eval conditionalExports := $(shell grep '^CONDITIONAL_EXPORT_LIST += $$($2)' make/defs.make | sed 's:.*($(2))/::g'))
    $(eval allExports := $(unconditionalExports) $(conditionalExports))
    $(foreach file,$(1),$(if $(findstring $(file),$(allExports)), ,$(error "Line matching '$(uncondPattern)$(file)' or '$(condPattern)$(file)' not found in $(defs)")))
    $(foreach export,$(unconditionalExports),$(if $(findstring $(export),$(1)), ,$(error "The line '$(uncondPattern)$(export)' should not be in $(defs)")))
endef

all: default
\t$(info Put $(EXPORTED_FILES) into SHARED_DIR $(SHARED_DIR))
\t$(shell mkdir -p $(SHARED_DIR))
\t$(foreach export,$(EXPORTED_FILES),$(call extract,$(export),$(SHARED_DIR)))

export: all
\t$(call verify_defs_make,$(notdir $(wildcard $(SHARED_DIR)/jvmci.services/*)),EXPORT_JRE_LIB_JVMCI_SERVICES_DIR)
\t$(call verify_defs_make,$(notdir $(wildcard $(SHARED_DIR)/jvmci.options/*)),EXPORT_JRE_LIB_JVMCI_OPTIONS_DIR)
.PHONY: export

clean:
\t$(QUIETLY) rm $(JARS) 2> /dev/null || true
\t$(QUIETLY) rmdir -p $(dir $(JARS)) 2> /dev/null || true
.PHONY: export clean

""")
    s = mx.suite("graal")
    dists = []
    ap = []
    projects = []
    for d in s.dists:
        if d.name in selectedDists:
            update_list(dists, d.get_dist_deps(True, True))
            update_list(projects, d.sorted_deps(includeLibs=False, transitive=True))

    for p in projects:
        deps = p.all_deps([], False, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=True)
        for d in deps:
            if d.definedAnnotationProcessorsDist is not None:
                apd = d.definedAnnotationProcessorsDist
                update_list(ap, [apd])

    if len(dists) > 0:
        mf.add_definition(jdkBootClassPathVariableName + " = " + bootClassPath)
        for d in ap: make_dist_rule(d, mf)
        for d in dists: make_dist_rule(d, mf)
        mf.add_definition("DISTRIBUTIONS = " + " ".join([short_dist_name(d.name) for d in dists+ap]))
        mf.add_rule("default: $({}_JAR)\n.PHONY: default\n".format("_JAR) $(".join([short_dist_name(d.name) for d in dists])))
        return True
    else:
        for d in dists:
            selectedDists.remove(d.name)
        print "Distribution(s) '" + "', '".join(selectedDists) + "' does not exist."