#!/usr/bin/python # # Parse Gradle module structure and generate resource files which are # used to bootstrap Gradle. # # First file, gradle-bootstrap-module-list, contains list of Gradle # modules sorted by dependencies. First module doesn't depend on any # other module. All subsequent modules can depend only on modules # listed above them. This is the order in which modules should be # compiled. # # A second file, gradle-bootstrap-module-dependencies, contains list # of dependecies for each module. # # Author: Mikolaj Izdebski import re import sys from glob import glob from zipfile import ZipFile def read_property(jar_path, props_name, prop_key): prefix = prop_key + "=" prefix_len = len(prefix) with ZipFile(jar_path) as jar: with jar.open(props_name, "rU") as props: for line in [line.rstrip() for line in props.readlines()]: if line.startswith(prefix) and line[prefix_len:]: return line[prefix_len:] class GradleModule(object): def __init__(self, path): self.name = re.match(r'.*/(.+)-[0-9.]*jar', path).group(1) self.path = path self.dependencies = [] def read_dependencies(self): projects = read_property(self.path, self.name + "-classpath.properties", "projects") if projects: self.dependencies = projects.split(",") def __eq__(self, other): return self.name == other.name def __hash__(self): return hash(self.name) # Read all Gradle modules from given Gradle home directory def read_gradle_modules(gradle_home): paths = [] for pattern in ["/lib/gradle-*.jar", "/lib/plugins/gradle-*.jar"]: paths.extend(glob(gradle_home + pattern)) return [GradleModule(path) for path in paths] # Resolve module dependencies to concrete modules def resolve_dependencies(modules, mapping): for module in modules: module.read_dependencies() resolved_deps = [] for dep in module.dependencies: resolved_dep = mapping.get(dep, None) if not resolved_dep: raise RuntimeError("Unresolved dependency from %s to %s" % (module.name, dep)) resolved_deps.append(resolved_dep) module.dependencies = resolved_deps # Sort modules in-situ, placing them in dependency order def topological_sort(modules): not_visited = set(modules) visiting = set() del modules[:] def visit(module): visiting.add(module) for dependency in module.dependencies: if dependency in visiting: raise RuntimeError("module dependency cycle detected") if dependency in not_visited: visit(dependency) modules.append(module) not_visited.remove(module) visiting.remove(module) while not_visited: visit(next(iter(not_visited))) return modules # Extract a single resource from given module def extract_resource(module, resource, target_file): with ZipFile(module.path) as jar: with open(target_file, "w") as f: f.write(jar.read(resource)) if len(sys.argv) != 2: sys.exit("Missing argument, usage: %s " % sys.argv[0]) gradle_home = sys.argv[1] modules = read_gradle_modules(gradle_home) if not modules: sys.exit("Unable to find any Gradle modules in specifed location") module_mapping = dict((module.name, module) for module in modules) resolve_dependencies(modules, module_mapping) topological_sort(modules) # Generate file with sorted module list with open("gradle-bootstrap-module-list", "w") as f: for module in modules: f.write("%s\n" % module.name) # Generate file with module dependencies with open("gradle-bootstrap-module-dependencies", "w") as f: for module in modules: f.write("%s=%s\n" % (module.name, ",".join(dep.name for dep in module.dependencies))) # Extract some other resoures from Gradle JARs extract_resource(module_mapping["gradle-docs"], "api-mapping.txt", "gradle-bootstrap-api-mapping.txt") extract_resource(module_mapping["gradle-docs"], "default-imports.txt", "gradle-bootstrap-default-imports.txt") extract_resource(module_mapping["gradle-core"], "gradle-plugins.properties", "gradle-bootstrap-plugin.properties") extract_resource(module_mapping["gradle-core"], "gradle-implementation-plugins.properties", "gradle-bootstrap-implementation-plugin.properties") extract_resource(module_mapping["gradle-runtime-api-info"], "org/gradle/api/internal/runtimeshaded/api-relocated.txt", "gradle-bootstrap-api-relocated.txt") extract_resource(module_mapping["gradle-runtime-api-info"], "org/gradle/api/internal/runtimeshaded/test-kit-relocated.txt", "gradle-bootstrap-test-kit-relocated.txt")