Esempio n. 1
0
 def get_add_exports(self):
     distributions = self.jar_distributions
     distributions_transitive = mx.classpath_entries(distributions)
     required_exports = mx_javamodules.requiredExports(
         distributions_transitive, base_jdk())
     return ' '.join(
         AbstractNativeImageConfig.get_add_exports_list(required_exports))
Esempio n. 2
0
 def suite_collector(suite, predicate, collector, javaProperties,
                     seenSuites):
     if suite.name in seenSuites:
         return
     seenSuites.add(suite.name)
     suite.visit_imports(import_visitor,
                         predicate=predicate,
                         collector=collector,
                         javaProperties=javaProperties,
                         seenSuites=seenSuites)
     for dist in suite.dists:
         if predicate(dist):
             for distCpEntry in mx.classpath_entries(dist):
                 if hasattr(distCpEntry, "getJavaProperties"):
                     for key, value in distCpEntry.getJavaProperties(
                     ).items():
                         javaProperties[key] = value
                 if distCpEntry.isJdkLibrary() or distCpEntry.isJreLibrary(
                 ):
                     cpPath = distCpEntry.classpath_repr(mx.get_jdk(),
                                                         resolve=True)
                 else:
                     cpPath = distCpEntry.classpath_repr(resolve=True)
                 if cpPath:
                     collector[cpPath] = None
Esempio n. 3
0
 def get_add_exports(self, missing_jars):
     if self.use_modules is None:
         return ''
     distributions = self.jar_distributions
     distributions_transitive = mx.classpath_entries(distributions)
     distributions_transitive_clean = [entry for entry in distributions_transitive if str(entry) not in missing_jars]
     required_exports = mx_javamodules.requiredExports(distributions_transitive_clean, base_jdk())
     return ' '.join(AbstractNativeImageConfig.get_add_exports_list(required_exports))
Esempio n. 4
0
def make_dist_rule(dist, mf):
    def path_dist_relative(p):
        return os.path.relpath(p, dist.suite.dir)
    jdkDeployedDists = get_jdk_deployed_dists()
    jarName = os.path.basename(dist.path)
    sourcesVariableName = dist.name + "_SRC"
    depJarVariableName = dist.name + "_DEP_JARS"
    sources = []
    resources = []
    projects = [p for p in dist.archived_deps() if p.isJavaProject()]
    targetPathPrefix = "$(TARGET)/"
    annotationProcessorDeps = set()

    classPath = [targetPathPrefix + os.path.basename(e.path) for e in mx.classpath_entries(dist, includeSelf=False, preferProjects=False)]
    for p in projects:
        projectDir = path_dist_relative(p.dir)
        annotationProcessorDeps.update(p.declaredAnnotationProcessors)
        depCheck = _get_dependency_check(p)
        if depCheck:
            sources.append(depCheck)
        for src in [projectDir + '/' + d for d in p.srcDirs]:
            sources.append(sourcesVariableName + " += $(shell find {} -type f 2> /dev/null)".format(src))
            metaInf = src + "/META-INF"
            if os.path.exists(os.path.join(dist.suite.dir, metaInf)):
                resources.append(metaInf)
        if depCheck:
            sources.append("endif")

    mf.add_definition("\n".join(sources))

    apDistNames = []
    apDistVariableNames = []
    apDependencies = []
    for apd in sorted(annotationProcessorDeps):
        apDistNames.append(apd.name)
        apDistVariableNames.append("$(" + apd.name + "_JAR)")
        apDependencies.append("$(subst  $(space),:,$(" + apd.name + "_DEP_JARS))")
    shouldExport = dist in jdkDeployedDists
    props = {
           "name": dist.name,
           "jarName": targetPathPrefix + jarName,
           "depJarsVariableAccess": "$(" + depJarVariableName + ")" if len(classPath) > 0 else "",
           "depJarsVariable": depJarVariableName,
           "sourcesVariableName": sourcesVariableName,
           "annotationProcessors": " ".join(apDistVariableNames),
           "cpAnnotationProcessors": ":".join(apDistVariableNames + apDependencies),
           "jarDeps": " ".join(classPath),
           "copyResources": " ".join(resources)
           }

    mf.add_definition("{name}_JAR = {jarName}".format(**props))
    if len(classPath) > 0: mf.add_definition("{depJarsVariable} = {jarDeps}".format(**props))
    if shouldExport: mf.add_definition("EXPORTED_FILES += $({name}_JAR)".format(**props))
    mf.add_rule("""$({name}_JAR): $({sourcesVariableName}) {annotationProcessors} {depJarsVariableAccess}
\t$(call build_and_jar,{cpAnnotationProcessors},$(subst  $(space),:,{depJarsVariableAccess}),{copyResources},$({name}_JAR))
""".format(**props))
    return
Esempio n. 5
0
 def suite_collector(suite, predicate, collector, javaProperties, seenSuites):
     if suite.name in seenSuites:
         return
     seenSuites.add(suite.name)
     suite.visit_imports(import_visitor, predicate=predicate, collector=collector, javaProperties=javaProperties, seenSuites=seenSuites)
     for dist in suite.dists:
         if predicate(dist):
             for distCpEntry in mx.classpath_entries(dist):
                 if hasattr(distCpEntry, "getJavaProperties"):
                     for key, value in dist.getJavaProperties().items():
                         javaProperties[key] = value
                 if distCpEntry.isJdkLibrary() or distCpEntry.isJreLibrary():
                     cpPath = distCpEntry.classpath_repr(mx.get_jdk(), resolve=True)
                 else:
                     cpPath = distCpEntry.classpath_repr(resolve=True)
                 if cpPath:
                     collector[cpPath] = None
Esempio n. 6
0
def make_java_module(dist, jdk):
    """
    Creates a Java module from a distribution.

    :param JARDistribution dist: the distribution from which to create a module
    :param JDKConfig jdk: a JDK with a version >= 9 that can be used to compile the module-info class
    :return: the `JavaModuleDescriptor` for the created Java module
    """
    info = get_java_module_info(dist)
    if info is None:
        return None

    moduleName, moduleDir, moduleJar = info  # pylint: disable=unpacking-non-sequence
    mx.log('Building Java module ' + moduleName + ' from ' + dist.name)
    exports = {}
    requires = {}
    concealedRequires = {}
    addExports = set()
    uses = set()

    modulepath = list()
    usedModules = set()

    if dist.suite.getMxCompatibility().moduleDepsEqualDistDeps():
        moduledeps = dist.archived_deps()
        for dep in mx.classpath_entries(dist, includeSelf=False):
            jmd = make_java_module(dep, jdk) if dep.isJARDistribution() else None
            if jmd:
                modulepath.append(jmd)
                requires[jmd.name] = set(['public'])
            elif (dep.isJdkLibrary() or dep.isJreLibrary()) and dep.is_provided_by(jdk):
                pass
            else:
                mx.abort(dist.name + ' cannot depend on ' + dep.name + ' as it does not define a module')
    else:
        moduledeps = get_module_deps(dist)

    # Prepend JDK modules to module path
    allmodules = list(jdk.get_modules()) + modulepath

    javaprojects = [d for d in moduledeps if d.isJavaProject()]
    packages = []
    for dep in javaprojects:
        uses.update(getattr(dep, 'uses', []))
        for pkg in itertools.chain(dep.imported_java_packages(projectDepsOnly=False), getattr(dep, 'imports', [])):
            depModule, visibility = lookup_package(allmodules, pkg, moduleName)
            if depModule:
                requires.setdefault(depModule.name, set())
                if visibility == 'exported':
                    # A distribution based module does not re-export its imported JDK packages
                    usedModules.add(depModule)
                else:
                    assert visibility == 'concealed'
                    concealedRequires.setdefault(depModule.name, set()).add(pkg)
                    usedModules.add(depModule)
                    addExports.add('--add-exports=' + depModule.name + '/' + pkg + '=' + moduleName)

        # If an "exports" attribute is not present, all packages are exported
        for package in _expand_package_info(dep, getattr(dep, 'exports', dep.defined_java_packages())):
            exports.setdefault(package, [])

        packages.extend(dep.defined_java_packages())

    provides = {}
    if exists(moduleDir):
        shutil.rmtree(moduleDir)
    for d in [dist] + [md for md in moduledeps if md.isJARDistribution()]:
        if d.isJARDistribution():
            with zipfile.ZipFile(d.path, 'r') as zf:
                # To compile module-info.java, all classes it references must either be given
                # as Java source files or already exist as class files in the output directory.
                # As such, the jar file for each constituent distribution must be unpacked
                # in the output directory.
                zf.extractall(path=moduleDir)
                names = frozenset(zf.namelist())
                for arcname in names:
                    if arcname.startswith('META-INF/services/') and not arcname == 'META-INF/services/':
                        service = arcname[len('META-INF/services/'):]
                        assert '/' not in service
                        provides.setdefault(service, set()).update(zf.read(arcname).splitlines())
                        # Service types defined in the module are assumed to be used by the module
                        serviceClass = service.replace('.', '/') + '.class'
                        if serviceClass in names:
                            uses.add(service)

    jmd = JavaModuleDescriptor(moduleName, exports, requires, uses, provides, packages=packages, concealedRequires=concealedRequires,
                               jarpath=moduleJar, dist=dist, modulepath=modulepath)

    # Compile module-info.class
    moduleInfo = join(moduleDir, 'module-info.java')
    with open(moduleInfo, 'w') as fp:
        print >> fp, jmd.as_module_info()
    javacCmd = [jdk.javac, '-d', moduleDir]
    modulepathJars = [m.jarpath for m in jmd.modulepath if m.jarpath]
    if modulepathJars:
        javacCmd.append('--module-path')
        javacCmd.append(os.pathsep.join(modulepathJars))
    javacCmd.extend(addExports)
    javacCmd.append(moduleInfo)
    mx.run(javacCmd)

    # Create the module jar
    shutil.make_archive(moduleJar, 'zip', moduleDir)
    os.rename(moduleJar + '.zip', moduleJar)
    jmd.save()
    return jmd
Esempio n. 7
0
    def build(self):
        if 'FASTR_RELEASE' not in os.environ:
            mx.log('FastR: set FASTR_RELEASE to update release project')
            return
        # copy the release directories
        output_dir = self.subject.dir
        fastr_dir = _fastr_suite.dir
        for d in ['bin', 'include', 'library', 'etc', 'share', 'doc']:
            target_dir = join(output_dir, d)
            if os.path.exists(target_dir):
                shutil.rmtree(target_dir)
            shutil.copytree(join(fastr_dir, d), target_dir)

        lib_fastr_dir = join(fastr_dir, 'lib')
        lib_output_dir = join(output_dir, 'lib')
        if os.path.exists(lib_output_dir):
            shutil.rmtree(lib_output_dir)
        os.mkdir(lib_output_dir)
        for f in os.listdir(lib_fastr_dir):
            source_file = join(lib_fastr_dir, f)
            target_file = join(lib_output_dir, f)
            if f != '.DS_Store':
                if os.path.islink(source_file):
                    os.symlink(os.readlink(source_file), target_file)
                else:
                    shutil.copy(source_file, target_file)

        # copyrights
        copyrights_dir = join(fastr_dir, 'mx.fastr', 'copyrights')
        with open(join(output_dir, 'COPYRIGHT'), 'w') as outfile:
            for copyright_file in os.listdir(copyrights_dir):
                if basename(copyright_file).endswith('copyright.star'):
                    with open(join(copyrights_dir, copyright_file)) as infile:
                        data = infile.read()
                        outfile.write(data)
        # license/README
        shutil.copy(join(fastr_dir, 'LICENSE'), output_dir)
        shutil.copy(join(fastr_dir, 'README.md'), output_dir)

        # canonicalize R_HOME_DIR in bin/R
        bin_dir = join(output_dir, 'bin')
        rcmd = join(bin_dir, 'R')
        # R is the generic shell script (taken essentially verbatim from GNU R)
        with open(rcmd) as f:
            lines = f.readlines()
        with open(rcmd, 'w') as f:
            for line in lines:
                if line.startswith('R_HOME_DIR='):
                    f.write("""
source="${BASH_SOURCE[0]}"
while [ -h "$source" ] ; do
  prev_source="$source"
  source="$(readlink "$source")";
  if [[ "$source" != /* ]]; then
    # if the link was relative, it was relative to where it came from
    dir="$( cd -P "$( dirname "$prev_source" )" && pwd )"
    source="$dir/$source"
  fi
done
r_bin="$( cd -P "$( dirname "$source" )" && pwd )"
R_HOME_DIR="$( dirname "$r_bin" )"
""")
                elif line.strip() == "#!/bin/sh":
                    f.write("#!/usr/bin/env bash\n")
                else:
                    f.write(line)
        # jar files for the launchers
        jars_dir = join(bin_dir, 'fastr_jars')
        if not os.path.exists(jars_dir):
            os.mkdir(jars_dir)

        # Copy all the jar files needed by the launchers
        for e in mx.classpath_entries('FASTR'):
            source_path = e.classpath_repr()
            if mx.is_cache_path(source_path):
                target_file_name = e.name.lower().replace('_', '-') + '.jar'
            else:
                target_file_name = basename(source_path)
            assert isfile(source_path)
            shutil.copy(source_path, join(jars_dir, target_file_name))

        # create the classpath string
        classpath = []
        for _, _, jars in os.walk(jars_dir):
            for jar in jars:
                classpath.append(join("$R_HOME/bin/fastr_jars", jar))
        classpath_string = ":".join(classpath)

        # replace the mx exec scripts with native Java launchers, setting the classpath from above
        bin_exec_dir = join(bin_dir, 'exec')
        r_launcher = join(self.subject.dir, 'src', 'R_legacy')
        template_dict = {'CLASSPATH': classpath_string}
        self._template(r_launcher, join(bin_exec_dir, 'R'), template_dict)
        shutil.rmtree(join(bin_dir, 'execRextras'))
        rscript_launcher = join(self.subject.dir, 'src', 'Rscript_legacy')
        self._template(rscript_launcher, join(bin_dir, 'Rscript'),
                       template_dict)
Esempio n. 8
0
def _unittest_config_participant(config):
    vmArgs, mainClass, mainClassArgs = config
    cpIndex, cp = mx.find_classpath_arg(vmArgs)
    if cp:
        cp = _uniqify(cp.split(os.pathsep))
        if isJDK8:
            # Remove entries from class path that are in Graal or on the boot class path
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath]:
                redundantClasspathEntries.update((d.output_dir() for d in dist.archived_deps() if d.isJavaProject()))
                redundantClasspathEntries.add(dist.path)
            cp = os.pathsep.join([e for e in cp if e not in redundantClasspathEntries])
            vmArgs[cpIndex] = cp
        else:
            deployedModules = []
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath] + _bootclasspath_appends:
                deployedModule = as_java_module(dist, jdk)
                deployedModules.append(deployedModule)
                redundantClasspathEntries.update(mx.classpath(dist, preferProjects=False, jdk=jdk).split(os.pathsep))
                redundantClasspathEntries.update(mx.classpath(dist, preferProjects=True, jdk=jdk).split(os.pathsep))

            # Remove entries from the class path that are in the deployed modules
            cp = [classpathEntry for classpathEntry in cp if classpathEntry not in redundantClasspathEntries]
            vmArgs[cpIndex] = os.pathsep.join(cp)

            # Junit libraries are made into automatic modules so that they are visible to tests
            # patched into modules. These automatic modules must be declared to be read by
            # Graal which means they must also be made root modules (i.e., ``-addmods``)
            # since ``-XaddReads`` can only be applied to root modules.
            junitCp = [e.classpath_repr() for e in mx.classpath_entries(['JUNIT'])]
            junitModules = [_automatic_module_name(e) for e in junitCp]
            vmArgs.extend(['-modulepath', os.pathsep.join(junitCp)])
            vmArgs.extend(['-addmods', ','.join(junitModules + [m.name for m in deployedModules])])
            for deployedModule in deployedModules:
                vmArgs.append('-XaddReads:' + deployedModule.name + '=' + ','.join(junitModules))

            # Explicitly export concealed JVMCI packages required by Graal. Even though
            # normally exported via jdk.vm.ci.services.Services.exportJVMCITo(), the
            # Junit harness wants to access JVMCI classes (e.g., when loading classes
            # to find test methods) without going through that entry point.
            addedExports = {}
            for deployedModule in deployedModules:
                for concealingModule, packages in deployedModule.concealedRequires.iteritems():
                    if concealingModule == 'jdk.vm.ci':
                        for package in packages:
                            addedExports.setdefault(concealingModule + '/' + package, set()).add(deployedModule.name)

            patches = {}
            pathToProject = {p.output_dir() : p for p in mx.projects() if p.isJavaProject()}
            for classpathEntry in cp:
                # Export concealed packages used by the class path entry
                _add_exports_for_concealed_packages(classpathEntry, pathToProject, addedExports, 'ALL-UNNAMED', deployedModules)

                for deployedModule in deployedModules:
                    assert deployedModule.dist.path != classpathEntry, deployedModule.dist.path + ' should no longer be on the class path'
                    # Patch the class path entry into a module if it defines packages already defined by the module.
                    # Packages definitions cannot be split between modules.
                    classpathEntryPackages = frozenset(_defined_packages(classpathEntry))
                    if not classpathEntryPackages.isdisjoint(deployedModule.packages):
                        patches.setdefault(deployedModule.name, []).append(classpathEntry)
                        extraPackages = classpathEntryPackages - frozenset(deployedModule.exports.iterkeys())
                        if extraPackages:
                            # From http://openjdk.java.net/jeps/261:
                            # If a package found in a module definition on a patch path is not already exported
                            # by that module then it will, still, not be exported. It can be exported explicitly
                            # via either the reflection API or the -XaddExports option.
                            for package in extraPackages:
                                addedExports.setdefault(deployedModule.name + '/' + package, set()).update(junitModules + ['ALL-UNNAMED'])

            for moduleName, cpEntries in patches.iteritems():
                vmArgs.append('-Xpatch:' + moduleName + '=' + os.pathsep.join(cpEntries))

            vmArgs.extend(['-XaddExports:' + export + '=' + ','.join(sorted(targets)) for export, targets in addedExports.iteritems()])

    if isJDK8:
        # Run the VM in a mode where application/test classes can
        # access JVMCI loaded classes.
        vmArgs.append('-XX:-UseJVMCIClassLoader')

    return (vmArgs, mainClass, mainClassArgs)
Esempio n. 9
0
def _unittest_config_participant(config):
    vmArgs, mainClass, mainClassArgs = config
    cpIndex, cp = mx.find_classpath_arg(vmArgs)
    if cp:
        cp = _uniqify(cp.split(os.pathsep))
        if isJDK8:
            # Remove entries from class path that are in Graal
            excluded = set()
            for entry in _jvmci_classpath:
                dist = entry.dist()
                excluded.update((d.output_dir() for d in dist.archived_deps() if d.isJavaProject()))
                excluded.add(dist.path)
            cp = os.pathsep.join([e for e in cp if e not in excluded])
            vmArgs[cpIndex] = cp
        else:
            # Remove entries from class path that are in the Graal
            assert _graal_module_descriptor is not None
            module = as_java_module(_graal_module_descriptor.dist(), jdk)

            junitCp = [e.classpath_repr() for e in mx.classpath_entries(['JUNIT'])]
            excluded = frozenset([classpathEntry.classpath_repr() for classpathEntry in get_module_deps(module.dist)] + junitCp)

            cp = [classpathEntry for classpathEntry in cp if classpathEntry not in excluded]

            vmArgs[cpIndex] = os.pathsep.join(cp)

            # Junit libraries are made into automatic modules so that they are visible to tests
            # patched into Graal. These automatic modules must be declared to be read by
            # Graal which means they must also be made root modules (i.e., ``-addmods``)
            # since ``-XaddReads`` can only be applied to root modules.
            junitModules = [_automatic_module_name(e) for e in junitCp]
            vmArgs.extend(['-modulepath', os.pathsep.join(junitCp)])
            vmArgs.extend(['-addmods', ','.join(junitModules)])
            vmArgs.extend(['-XaddReads:' + module.name + '=' + ','.join(junitModules)])

            # Explicitly export JVMCI to Graal
            addedExports = {}
            for concealingModule, packages in module.concealedRequires.iteritems():
                if concealingModule == 'jdk.vm.ci':
                    for package in packages:
                        addedExports.setdefault(concealingModule + '/' + package, set()).add(module.name)

            patches = []
            graalConcealedPackages = list(module.conceals)
            pathToProject = {p.output_dir() : p for p in mx.projects() if p.isJavaProject()}
            for classpathEntry in cp:
                # Export concealed JDK packages used by the class path entry
                _add_exports_for_concealed_packages(classpathEntry, pathToProject, addedExports, 'ALL-UNNAMED')

                # Patch the class path entry into Graal if it defines packages already defined by Graal.
                # Packages definitions cannot be split between modules.
                packages = frozenset(_defined_packages(classpathEntry))
                if not packages.isdisjoint(module.packages):
                    patches.append(classpathEntry)
                    extraPackages = packages - module.packages
                    if extraPackages:
                        # From http://openjdk.java.net/jeps/261:
                        # If a package found in a module definition on a patch path is not already exported
                        # by that module then it will, still, not be exported. It can be exported explicitly
                        # via either the reflection API or the -XaddExports option.
                        graalConcealedPackages.extend(extraPackages)

            if patches:
                vmArgs.append('-Xpatch:' + module.name + '=' + os.pathsep.join(patches))

            # Export all Graal packages to make them available to test classes
            for package in graalConcealedPackages:
                addedExports.setdefault(module.name + '/' + package, set()).update(junitModules + ['ALL-UNNAMED'])

            vmArgs.extend(['-XaddExports:' + export + '=' + ','.join(sorted(targets)) for export, targets in addedExports.iteritems()])

    if isJDK8:
        # Run the VM in a mode where application/test classes can
        # access JVMCI loaded classes.
        vmArgs.append('-XX:-UseJVMCIClassLoader')

    return (vmArgs, mainClass, mainClassArgs)
Esempio n. 10
0
def _unittest_config_participant(config):
    vmArgs, mainClass, mainClassArgs = config
    cpIndex, cp = mx.find_classpath_arg(vmArgs)
    if cp:
        cp = _uniqify(cp.split(os.pathsep))
        if isJDK8:
            # Remove entries from class path that are in Graal or on the boot class path
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath]:
                redundantClasspathEntries.update((d.output_dir() for d in dist.archived_deps() if d.isJavaProject()))
                redundantClasspathEntries.add(dist.path)
            cp = os.pathsep.join([e for e in cp if e not in redundantClasspathEntries])
            vmArgs[cpIndex] = cp
        else:
            deployedModules = []
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath] + _bootclasspath_appends:
                deployedModule = as_java_module(dist, jdk)
                deployedModules.append(deployedModule)
                redundantClasspathEntries.update(mx.classpath(dist, preferProjects=False, jdk=jdk).split(os.pathsep))
                redundantClasspathEntries.update(mx.classpath(dist, preferProjects=True, jdk=jdk).split(os.pathsep))
                if hasattr(dist, 'overlaps'):
                    for o in dist.overlaps:
                        path = mx.distribution(o).classpath_repr()
                        if path:
                            redundantClasspathEntries.add(path)

            # Remove entries from the class path that are in the deployed modules
            cp = [classpathEntry for classpathEntry in cp if classpathEntry not in redundantClasspathEntries]
            vmArgs[cpIndex] = os.pathsep.join(cp)

            # Junit libraries are made into automatic modules so that they are visible to tests
            # patched into modules. These automatic modules must be declared to be read by
            # Graal which means they must also be made root modules (i.e., ``--add-modules``)
            # since ``--add-reads`` can only be applied to root modules.
            junitCp = [e.classpath_repr() for e in mx.classpath_entries(['JUNIT'])]
            junitModules = [_automatic_module_name(e) for e in junitCp]
            vmArgs.append('--module-path=' + os.pathsep.join(junitCp))
            vmArgs.append('--add-modules=' + ','.join(junitModules + [m.name for m in deployedModules]))
            for deployedModule in deployedModules:
                vmArgs.append('--add-reads=' + deployedModule.name + '=' + ','.join(junitModules))

            # Explicitly export concealed JVMCI packages required by Graal. Even though
            # normally exported via jdk.vm.ci.services.Services.exportJVMCITo(), the
            # Junit harness wants to access JVMCI classes (e.g., when loading classes
            # to find test methods) without going through that entry point.
            addedExports = {}
            for deployedModule in deployedModules:
                for concealingModule, packages in deployedModule.concealedRequires.iteritems():
                    if concealingModule == 'jdk.vm.ci':
                        for package in packages:
                            addedExports.setdefault(concealingModule + '/' + package, set()).add(deployedModule.name)

            pathToDep = {p.output_dir() if p.isJavaProject() else p.path: p for p in mx.dependencies() if p.isJavaProject() or p.isJARDistribution()}
            for classpathEntry in cp:
                # Export concealed packages used by the class path entry
                _add_exports_for_concealed_packages(classpathEntry, pathToDep, addedExports, 'ALL-UNNAMED', deployedModules)

                for deployedModule in deployedModules:
                    assert deployedModule.dist.path != classpathEntry, deployedModule.dist.path + ' should no longer be on the class path'
                    # Ensure the class path entry does not define packages already defined by the module.
                    # Packages definitions cannot be split between modules.
                    classpathEntryPackages = frozenset(_defined_packages(classpathEntry))
                    intersection = classpathEntryPackages.intersection(deployedModule.packages)
                    if intersection:
                        mx.abort(classpathEntry + ' cannot extend package(s) defined in the module ' + deployedModule.name + ': ' + ', '.join(intersection))

            vmArgs.extend(['--add-exports=' + export + '=' + ','.join(sorted(targets)) for export, targets in addedExports.iteritems()])

    if isJDK8:
        # Run the VM in a mode where application/test classes can
        # access JVMCI loaded classes.
        vmArgs.append('-XX:-UseJVMCIClassLoader')

    return (vmArgs, mainClass, mainClassArgs)
Esempio n. 11
0
    def build(self):
        # copy the release directories
        output_dir = self.subject.dir
        fastr_dir = _fastr_suite.dir
        for d in ['bin', 'include', 'library', 'etc', 'share', 'doc']:
            target_dir = join(output_dir, d)
            source_dir = join(fastr_dir, d)
            if os.path.exists(target_dir):
                shutil.rmtree(target_dir)
            shutil.copytree(source_dir, target_dir)

        lib_fastr_dir = join(fastr_dir, 'lib')
        lib_output_dir = join(output_dir, 'lib')
        if os.path.exists(lib_output_dir):
            shutil.rmtree(lib_output_dir)
        os.mkdir(lib_output_dir)
        for f in os.listdir(lib_fastr_dir):
            source_file = join(lib_fastr_dir, f)
            target_file = join(lib_output_dir, f)
            if f != '.DS_Store':
                if os.path.islink(source_file):
                    os.symlink(os.readlink(source_file), target_file)
                else:
                    shutil.copy(source_file, target_file)

        # copyrights
        copyrights_dir = join(fastr_dir, 'mx.fastr', 'copyrights')
        with open(join(output_dir, 'COPYRIGHT'), 'w') as outfile:
            for copyright_file in os.listdir(copyrights_dir):
                if basename(copyright_file).endswith('copyright.star'):
                    with open(join(copyrights_dir, copyright_file)) as infile:
                        data = infile.read()
                        outfile.write(data)
        # license/README
        shutil.copy(join(fastr_dir, 'LICENSE'), output_dir)
        shutil.copy(join(fastr_dir, 'README.md'), output_dir)

        # jar files for the launchers
        bin_dir = join(output_dir, 'bin')
        jars_dir = join(bin_dir, 'fastr_jars')
        if not os.path.exists(jars_dir):
            os.mkdir(jars_dir)

        # Copy all the jar files needed by the launchers
        for e in mx.classpath_entries('FASTR'):
            source_path = e.classpath_repr()
            if mx.is_cache_path(source_path):
                target_file_name = e.name.lower().replace('_', '-') + '.jar'
            else:
                target_file_name = basename(source_path)
            assert isfile(source_path)
            shutil.copy(source_path, join(jars_dir, target_file_name))

        # create the classpath string
        classpath = []
        for _, _, jars in os.walk(jars_dir):
            for jar in jars:
                classpath.append(join("$R_HOME/bin/fastr_jars", jar))
        classpath_string = ":".join(classpath)

        # replace the mx exec scripts with native Java launchers, setting the classpath from above
        bin_exec_dir = join(bin_dir, 'exec')
        r_launcher = join(self.subject.dir, 'src', 'R_launcher')
        template_dict = {'CLASSPATH': classpath_string}
        self._template(r_launcher, join(bin_exec_dir, 'R'), template_dict)
        shutil.rmtree(join(bin_dir, 'execRextras'))
        rscript_launcher = join(self.subject.dir, 'src', 'Rscript_launcher')
        self._template(rscript_launcher, join(bin_dir, 'Rscript'),
                       template_dict)
Esempio n. 12
0
def make_java_module(dist, jdk):
    """
    Creates a Java module from a distribution.

    :param JARDistribution dist: the distribution from which to create a module
    :param JDKConfig jdk: a JDK with a version >= 9 that can be used to compile the module-info class
    :return: the `JavaModuleDescriptor` for the created Java module
    """
    info = get_java_module_info(dist)
    if info is None:
        return None

    moduleName, moduleDir, moduleJar = info  # pylint: disable=unpacking-non-sequence
    mx.log('Building Java module ' + moduleName + ' from ' + dist.name)
    exports = {}
    requires = {}
    concealedRequires = {}
    uses = set()

    modulepath = list()
    usedModules = set()

    if dist.suite.getMxCompatibility().moduleDepsEqualDistDeps():
        moduledeps = dist.archived_deps()
        for dep in mx.classpath_entries(dist, includeSelf=False):
            if dep.isJARDistribution():
                jmd = as_java_module(
                    dep, jdk, fatalIfNotCreated=False) or make_java_module(
                        dep, jdk)
                modulepath.append(jmd)
                requires[jmd.name] = {jdk.get_transitive_requires_keyword()}
            elif (dep.isJdkLibrary()
                  or dep.isJreLibrary()) and dep.is_provided_by(jdk):
                pass
            else:
                mx.abort(dist.name + ' cannot depend on ' + dep.name +
                         ' as it does not define a module')
    else:
        moduledeps = get_module_deps(dist)

    # Append JDK modules to module path
    jdkModules = jdk.get_modules()
    if not isinstance(jdkModules, list):
        jdkModules = list(jdkModules)
    allmodules = modulepath + jdkModules

    javaprojects = [d for d in moduledeps if d.isJavaProject()]

    # Collect packages in the module first
    packages = set()
    for dep in javaprojects:
        packages.update(dep.defined_java_packages())

    for dep in javaprojects:
        uses.update(getattr(dep, 'uses', []))
        for pkg in getattr(dep, 'runtimeDeps', []):
            requires.setdefault(pkg, set(['static']))

        for pkg in itertools.chain(
                dep.imported_java_packages(projectDepsOnly=False),
                getattr(dep, 'imports', [])):
            # Only consider packages not defined by the module we're creating. This handles the
            # case where we're creating a module that will upgrade an existing upgradeable
            # module in the JDK such as jdk.internal.vm.compiler.
            if pkg not in packages:
                depModule, visibility = lookup_package(allmodules, pkg,
                                                       moduleName)
                if depModule and depModule.name != moduleName:
                    requires.setdefault(depModule.name, set())
                    if visibility == 'exported':
                        # A distribution based module does not re-export its imported JDK packages
                        usedModules.add(depModule)
                    else:
                        assert visibility == 'concealed'
                        concealedRequires.setdefault(depModule.name,
                                                     set()).add(pkg)
                        usedModules.add(depModule)

        # If an "exports" attribute is not present, all packages are exported
        for package in _expand_package_info(
                dep, getattr(dep, 'exports', dep.defined_java_packages())):
            exports.setdefault(package, [])

    provides = {}
    if exists(moduleDir):
        shutil.rmtree(moduleDir)
    for d in [dist] + [md for md in moduledeps if md.isJARDistribution()]:
        if d.isJARDistribution():
            with zipfile.ZipFile(d.path, 'r') as zf:
                # To compile module-info.java, all classes it references must either be given
                # as Java source files or already exist as class files in the output directory.
                # As such, the jar file for each constituent distribution must be unpacked
                # in the output directory.
                zf.extractall(path=moduleDir)
                names = frozenset(zf.namelist())

                # Flatten versioned resources
                versionsDir = join(moduleDir, 'META-INF', 'versions')
                if exists(versionsDir):
                    versionedRE = re.compile(
                        r'META-INF/versions/([1-9][0-9]*)/(.+)')
                    versions = {}
                    for arcname in sorted(names):
                        m = versionedRE.match(arcname)
                        if m:
                            version = int(m.group(1))
                            unversionedName = m.group(2)
                            versions.setdefault(
                                version,
                                {})[unversionedName] = zf.read(arcname)

                    for version, resources in sorted(versions.iteritems()):
                        for unversionedName, content in resources.iteritems():
                            dst = join(moduleDir, unversionedName)
                            if version <= jdk.javaCompliance.value:
                                parent = dirname(dst)
                                if parent and not exists(parent):
                                    os.makedirs(parent)
                                with open(dst, 'wb') as fp:
                                    fp.write(content)
                            else:
                                # Ignore resource whose version is too high
                                pass

                    shutil.rmtree(versionsDir)
                    manifest = join(moduleDir, 'META-INF/MANIFEST.MF')
                    # Remove Multi-Release attribute from manifest as the jar
                    # is now flattened. This is also a workaround for
                    # https://bugs.openjdk.java.net/browse/JDK-8193802
                    if exists(manifest):
                        with open(manifest) as fp:
                            content = fp.readlines()
                            newContent = [
                                l for l in content if not 'Multi-Release:' in l
                            ]
                        if newContent != content:
                            with open(manifest, 'w') as fp:
                                fp.write(''.join(newContent))

                serviceRE = re.compile(r'META-INF/services/(.+)')
                for arcname in names:
                    m = serviceRE.match(arcname)
                    if m:
                        service = m.group(1)

                        # While a META-INF provider configuration file must use a fully qualified binary
                        # name[1] of the service, a provides directive in a module descriptor must use
                        # the fully qualified non-binary name[2] of the service.
                        #
                        # [1] https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html
                        # [2] https://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleDescriptor.Provides.html#service--
                        service = service.replace('$', '.')

                        assert '/' not in service
                        provides.setdefault(service, set()).update(
                            zf.read(arcname).splitlines())
                        # Service types defined in the module are assumed to be used by the module
                        serviceClass = service.replace('.', '/') + '.class'
                        if serviceClass in names:
                            uses.add(service)
                servicesDir = join(moduleDir, 'META-INF', 'services')
                if exists(servicesDir):
                    shutil.rmtree(servicesDir)

    jmd = JavaModuleDescriptor(moduleName,
                               exports,
                               requires,
                               uses,
                               provides,
                               packages=packages,
                               concealedRequires=concealedRequires,
                               jarpath=moduleJar,
                               dist=dist,
                               modulepath=modulepath)

    # Compile module-info.class
    moduleInfo = join(moduleDir, 'module-info.java')
    with open(moduleInfo, 'w') as fp:
        print >> fp, jmd.as_module_info()
    javacCmd = [jdk.javac, '-d', moduleDir]
    jdkModuleNames = [m.name for m in jdkModules]
    modulepathJars = [
        m.jarpath for m in jmd.modulepath
        if m.jarpath and m.name not in jdkModuleNames
    ]
    upgrademodulepathJars = [
        m.jarpath for m in jmd.modulepath
        if m.jarpath and m.name in jdkModuleNames
    ]
    if modulepathJars:
        javacCmd.append('--module-path')
        javacCmd.append(os.pathsep.join(modulepathJars))
    if upgrademodulepathJars:
        javacCmd.append('--upgrade-module-path')
        javacCmd.append(os.pathsep.join(upgrademodulepathJars))
    if concealedRequires:
        for module, packages in concealedRequires.iteritems():
            for package in packages:
                javacCmd.append('--add-exports=' + module + '/' + package +
                                '=' + moduleName)
    javacCmd.append(moduleInfo)
    mx.run(javacCmd)

    # Create the module jar
    shutil.make_archive(moduleJar, 'zip', moduleDir)
    os.rename(moduleJar + '.zip', moduleJar)
    jmd.save()
    return jmd
Esempio n. 13
0
def _unittest_config_participant(config):
    vmArgs, mainClass, mainClassArgs = config
    cpIndex, cp = mx.find_classpath_arg(vmArgs)
    if cp:
        cp = _uniqify(cp.split(os.pathsep))
        if isJDK8:
            # Remove entries from class path that are in Graal or on the boot class path
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath]:
                redundantClasspathEntries.update(
                    (d.output_dir() for d in dist.archived_deps()
                     if d.isJavaProject()))
                redundantClasspathEntries.add(dist.path)
            cp = os.pathsep.join(
                [e for e in cp if e not in redundantClasspathEntries])
            vmArgs[cpIndex] = cp
        else:
            deployedModules = []
            redundantClasspathEntries = set()
            for dist in [entry.dist() for entry in _jvmci_classpath
                         ] + _bootclasspath_appends:
                deployedModule = as_java_module(dist, jdk)
                deployedModules.append(deployedModule)
                redundantClasspathEntries.update(
                    mx.classpath(dist, preferProjects=False,
                                 jdk=jdk).split(os.pathsep))
                redundantClasspathEntries.update(
                    mx.classpath(dist, preferProjects=True,
                                 jdk=jdk).split(os.pathsep))
                if hasattr(dist, 'overlaps'):
                    for o in dist.overlaps:
                        path = mx.distribution(o).classpath_repr()
                        if path:
                            redundantClasspathEntries.add(path)

            # Remove entries from the class path that are in the deployed modules
            cp = [
                classpathEntry for classpathEntry in cp
                if classpathEntry not in redundantClasspathEntries
            ]
            vmArgs[cpIndex] = os.pathsep.join(cp)

            # Junit libraries are made into automatic modules so that they are visible to tests
            # patched into modules. These automatic modules must be declared to be read by
            # Graal which means they must also be made root modules (i.e., ``--add-modules``)
            # since ``--add-reads`` can only be applied to root modules.
            junitCp = [
                e.classpath_repr() for e in mx.classpath_entries(['JUNIT'])
            ]
            junitModules = [_automatic_module_name(e) for e in junitCp]
            vmArgs.append('--module-path=' + os.pathsep.join(junitCp))
            vmArgs.append('--add-modules=' +
                          ','.join(junitModules +
                                   [m.name for m in deployedModules]))
            for deployedModule in deployedModules:
                vmArgs.append('--add-reads=' + deployedModule.name + '=' +
                              ','.join(junitModules))

            # Explicitly export concealed JVMCI packages required by Graal. Even though
            # normally exported via jdk.vm.ci.services.Services.exportJVMCITo(), the
            # Junit harness wants to access JVMCI classes (e.g., when loading classes
            # to find test methods) without going through that entry point.
            addedExports = {}
            for deployedModule in deployedModules:
                for concealingModule, packages in deployedModule.concealedRequires.iteritems(
                ):
                    if concealingModule == 'jdk.vm.ci':
                        for package in packages:
                            addedExports.setdefault(
                                concealingModule + '/' + package,
                                set()).add(deployedModule.name)

            pathToDep = {
                p.output_dir() if p.isJavaProject() else p.path: p
                for p in mx.dependencies()
                if p.isJavaProject() or p.isJARDistribution()
            }
            for classpathEntry in cp:
                # Export concealed packages used by the class path entry
                _add_exports_for_concealed_packages(classpathEntry, pathToDep,
                                                    addedExports,
                                                    'ALL-UNNAMED',
                                                    deployedModules)

                for deployedModule in deployedModules:
                    assert deployedModule.dist.path != classpathEntry, deployedModule.dist.path + ' should no longer be on the class path'
                    # Ensure the class path entry does not define packages already defined by the module.
                    # Packages definitions cannot be split between modules.
                    classpathEntryPackages = frozenset(
                        _defined_packages(classpathEntry))
                    intersection = classpathEntryPackages.intersection(
                        deployedModule.packages)
                    if intersection:
                        mx.abort(
                            classpathEntry +
                            ' cannot extend package(s) defined in the module '
                            + deployedModule.name + ': ' +
                            ', '.join(intersection))

            vmArgs.extend([
                '--add-exports=' + export + '=' + ','.join(sorted(targets))
                for export, targets in addedExports.iteritems()
            ])

    if isJDK8:
        # Run the VM in a mode where application/test classes can
        # access JVMCI loaded classes.
        vmArgs.append('-XX:-UseJVMCIClassLoader')

    return (vmArgs, mainClass, mainClassArgs)
Esempio n. 14
0
def make_java_module(dist, jdk):
    """
    Creates a Java module from a distribution.
    This updates the JAR by adding `module-info` classes.

    The `META-INF` directory can not be versioned. However, we make an exception here for `META-INF/services`:
    if different versions should have different service providers, a `META-INF/_versions/<version>/META-INF/services`
    directory can be used (note the `_` before `versions`).
    These service provider declarations will be used to build the versioned module-info files and the
    `META-INF/_versions/<version>` directories will be removed from the archive.
    This is done using a separate versioning directory so that the JAR can be a valid multi-release JAR before this
    transformation.

    input:
        com/foo/MyProvider.class                                    # JDK 8 or earlier specific provider
        META-INF/services/com.foo.MyService                         # Contains: com.foo.MyProvider
        META-INF/_versions/9/META-INF/services/com.foo.MyService    # Contains: com.foo.MyProvider
        META-INF/versions/9/com/foo/MyProvider.class                # JDK 9 and 10 specific provider
        META-INF/_versions/11/META-INF/services/com.foo.MyService   # Contains: provides com.foo.MyService with com.foo.MyProvider
        META-INF/versions/11/com/foo/MyProvider.class               # JDK 11 and later specific provider

    output:
        com/foo/MyProvider.class                        # JDK 8 or earlier specific provider
        META-INF/services/com.foo.MyService             # Contains: com.foo.MyProvider
        META-INF/versions/9/module-info.class           # Contains: provides com.foo.MyService with com.foo.MyProvider
        META-INF/versions/9/com/foo/MyProvider.class    # JDK 9 and 10 specific provider
        META-INF/versions/11/module-info.class          # Contains: provides com.foo.MyService with com.foo.MyProvider
        META-INF/versions/11/com/foo/MyProvider.class   # JDK 11 and later specific provider

    :param JARDistribution dist: the distribution from which to create a module
    :param JDKConfig jdk: a JDK with a version >= 9 that can be used to compile the module-info class
    :return: the `JavaModuleDescriptor` for the created Java module
    """
    info = get_java_module_info(dist)
    if info is None:
        return None

    moduleName, _, moduleJar = info  # pylint: disable=unpacking-non-sequence
    mx.log('Building Java module ' + moduleName + ' from ' + dist.name)
    exports = {}
    requires = {}
    concealedRequires = {}
    base_uses = set()

    modulepath = list()
    usedModules = set()

    if dist.suite.getMxCompatibility().moduleDepsEqualDistDeps():
        moduledeps = dist.archived_deps()
        for dep in mx.classpath_entries(dist, includeSelf=False):
            if dep.isJARDistribution():
                jmd = as_java_module(dep, jdk)
                modulepath.append(jmd)
                requires[jmd.name] = {jdk.get_transitive_requires_keyword()}
            elif (dep.isJdkLibrary()
                  or dep.isJreLibrary()) and dep.is_provided_by(jdk):
                pass
            elif dep.isLibrary():
                jmd = get_library_as_module(dep, jdk)
                modulepath.append(jmd)
                requires[jmd.name] = set()
            else:
                mx.abort(dist.name + ' cannot depend on ' + dep.name +
                         ' as it does not define a module')
    else:
        moduledeps = get_module_deps(dist)

    # Append JDK modules to module path
    jdkModules = jdk.get_modules()
    if not isinstance(jdkModules, list):
        jdkModules = list(jdkModules)
    allmodules = modulepath + jdkModules

    javaprojects = [d for d in moduledeps if d.isJavaProject()]

    # Collect packages in the module first
    packages = set()
    for dep in javaprojects:
        packages.update(dep.defined_java_packages())

    for dep in javaprojects:
        base_uses.update(getattr(dep, 'uses', []))
        for pkg in getattr(dep, 'runtimeDeps', []):
            requires.setdefault(pkg, {'static'})

        for pkg in itertools.chain(
                dep.imported_java_packages(projectDepsOnly=False),
                getattr(dep, 'imports', [])):
            # Only consider packages not defined by the module we're creating. This handles the
            # case where we're creating a module that will upgrade an existing upgradeable
            # module in the JDK such as jdk.internal.vm.compiler.
            if pkg not in packages:
                depModule, visibility = lookup_package(allmodules, pkg,
                                                       moduleName)
                if depModule and depModule.name != moduleName:
                    requires.setdefault(depModule.name, set())
                    if visibility == 'exported':
                        # A distribution based module does not re-export its imported JDK packages
                        usedModules.add(depModule)
                    else:
                        assert visibility == 'concealed'
                        concealedRequires.setdefault(depModule.name,
                                                     set()).add(pkg)
                        usedModules.add(depModule)

        # If an "exports" attribute is not present, all packages are exported
        for package in _expand_package_info(
                dep, getattr(dep, 'exports', dep.defined_java_packages())):
            if ' to ' in package:
                splitpackage = package.split(' to ')
                package = splitpackage[0].strip()
                if not package:
                    mx.abort(
                        'exports attribute cannot have empty package value',
                        context=dist)
                targets = [n.strip() for n in splitpackage[1].split(',')]
                if not targets:
                    mx.abort(
                        'exports attribute must have at least one target for qualified export',
                        context=dist)
                exports.setdefault(package, targets)
            else:
                exports.setdefault(package, [])

    work_directory = mkdtemp()
    try:
        files_to_remove = set()

        # To compile module-info.java, all classes it references must either be given
        # as Java source files or already exist as class files in the output directory.
        # As such, the jar file for each constituent distribution must be unpacked
        # in the output directory.
        versions = {}
        for d in [dist] + [md for md in moduledeps if md.isJARDistribution()]:
            if d.isJARDistribution():
                with zipfile.ZipFile(d.original_path(), 'r') as zf:
                    for arcname in sorted(zf.namelist()):
                        m = _versioned_re.match(arcname)
                        if m:
                            version = m.group(1)
                            unversioned_name = m.group(2)
                            if version <= jdk.javaCompliance:
                                versions.setdefault(
                                    version, {})[unversioned_name] = arcname
                            else:
                                # Ignore resource whose version is too high
                                pass
                            if unversioned_name.startswith(
                                    'META-INF/services/'):
                                files_to_remove.add(arcname)
                            elif unversioned_name.startswith('META-INF/'):
                                mx.abort(
                                    "META-INF resources can not be versioned and will make modules fail to load ({})."
                                    .format(arcname))
        default_jmd = None

        all_versions = set(versions.keys())
        if '9' not in all_versions:
            # 9 is the first version that supports modules and can be versioned in the JAR:
            # if there is no `META-INF/versions/9` then we should add a `module-info.class` to the root of the JAR
            # so that the module works on JDK 9.
            all_versions = all_versions | {'common'}
            default_version = 'common'
        else:
            default_version = str(max((int(v) for v in all_versions)))

        for version in all_versions:
            uses = base_uses.copy()
            provides = {}
            dest_dir = join(work_directory, version)
            int_version = int(version) if version != 'common' else -1

            for d in [dist
                      ] + [md for md in moduledeps if md.isJARDistribution()]:
                if d.isJARDistribution():
                    with zipfile.ZipFile(d.original_path(), 'r') as zf:
                        for name in zf.namelist():
                            m = _versioned_re.match(name)
                            if m:
                                file_version = int(m.group(1))
                                if file_version > int_version:
                                    continue
                                unversioned_name = m.group(2)
                                if name.startswith(_special_versioned_prefix):
                                    if not unversioned_name.startswith(
                                            'META-INF/services'):
                                        raise mx.abort(
                                            "The special versioned directory ({}) is only supported for META-INF/services files. Got {}"
                                            .format(_special_versioned_prefix,
                                                    name))
                                if unversioned_name:
                                    dst = join(dest_dir, unversioned_name)
                                    parent = dirname(dst)
                                    if parent and not exists(parent):
                                        os.makedirs(parent)
                                    with open(dst, 'wb') as fp:
                                        fp.write(zf.read(name))
                            else:
                                zf.extract(name, dest_dir)

                        servicesDir = join(dest_dir, 'META-INF', 'services')
                        if exists(servicesDir):
                            for servicePathName in os.listdir(servicesDir):
                                # While a META-INF provider configuration file must use a fully qualified binary
                                # name[1] of the service, a provides directive in a module descriptor must use
                                # the fully qualified non-binary name[2] of the service.
                                #
                                # [1] https://docs.oracle.com/javase/9/docs/api/java/util/ServiceLoader.html
                                # [2] https://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleDescriptor.Provides.html#service--
                                service = servicePathName.replace('$', '.')

                                assert '/' not in service
                                with open(join(servicesDir,
                                               servicePathName)) as fp:
                                    serviceContent = fp.read()
                                provides.setdefault(service, set()).update(
                                    serviceContent.splitlines())
                                # Service types defined in the module are assumed to be used by the module
                                serviceClassfile = service.replace(
                                    '.', '/') + '.class'
                                if exists(join(dest_dir, serviceClassfile)):
                                    uses.add(service)

            jmd = JavaModuleDescriptor(moduleName,
                                       exports,
                                       requires,
                                       uses,
                                       provides,
                                       packages=packages,
                                       concealedRequires=concealedRequires,
                                       jarpath=moduleJar,
                                       dist=dist,
                                       modulepath=modulepath)

            # Compile module-info.class
            module_info_java = join(dest_dir, 'module-info.java')
            with open(module_info_java, 'w') as fp:
                print(jmd.as_module_info(), file=fp)
            javacCmd = [jdk.javac, '-d', dest_dir]
            jdkModuleNames = [m.name for m in jdkModules]
            modulepathJars = [
                m.jarpath for m in jmd.modulepath
                if m.jarpath and m.name not in jdkModuleNames
            ]
            upgrademodulepathJars = [
                m.jarpath for m in jmd.modulepath
                if m.jarpath and m.name in jdkModuleNames
            ]
            # TODO we should rather use the right JDK
            javacCmd += [
                '-target', version if version != 'common' else '9', '-source',
                version if version != 'common' else '9'
            ]
            if modulepathJars:
                javacCmd.append('--module-path')
                javacCmd.append(os.pathsep.join(modulepathJars))
            if upgrademodulepathJars:
                javacCmd.append('--upgrade-module-path')
                javacCmd.append(os.pathsep.join(upgrademodulepathJars))
            if concealedRequires:
                for module, packages_ in concealedRequires.items():
                    for package in packages_:
                        javacCmd.append('--add-exports=' + module + '/' +
                                        package + '=' + moduleName)
            # https://blogs.oracle.com/darcy/new-javac-warning-for-setting-an-older-source-without-bootclasspath
            # Disable the "bootstrap class path not set in conjunction with -source N" warning
            # as we're relying on the Java compliance of project to correctly specify a JDK range
            # providing the API required by the project. Also disable the warning about unknown
            # modules in qualified exports (not sure how to avoid these since we build modules
            # separately).
            javacCmd.append('-Xlint:-options,-module')
            javacCmd.append(module_info_java)
            mx.run(javacCmd)
            module_info_class = join(dest_dir, 'module-info.class')

            # Append the module-info.class
            module_info_arc_dir = ''
            if version != 'common':
                module_info_arc_dir = _versioned_prefix + version + '/'
            if version == default_version:
                default_jmd = jmd

            with ZipFile(moduleJar, 'a') as zf:
                zf.write(module_info_class,
                         module_info_arc_dir + basename(module_info_class))
                zf.write(module_info_java,
                         module_info_arc_dir + basename(module_info_java))

        if files_to_remove:
            with mx.SafeFileCreation(moduleJar) as sfc:
                with ZipFile(moduleJar,
                             'r') as inzf, ZipFile(sfc.tmpPath, 'w',
                                                   inzf.compression) as outzf:
                    for info in inzf.infolist():
                        if info.filename not in files_to_remove:
                            outzf.writestr(info, inzf.read(info))
    finally:
        shutil.rmtree(work_directory)
    default_jmd.save()
    return default_jmd