| // Defines tasks for building the VDL tool and generating VDL files. |
| |
| package io.v.vdl |
| |
| import org.gradle.api.InvalidUserDataException |
| import org.gradle.api.Plugin |
| import org.gradle.api.Project |
| import org.gradle.api.artifacts.ProjectDependency |
| import org.gradle.api.file.FileCollection |
| import org.gradle.api.tasks.Copy |
| import org.gradle.api.tasks.Delete |
| import org.gradle.api.tasks.Exec |
| import org.gradle.language.jvm.tasks.ProcessResources |
| |
| import java.util.jar.JarEntry |
| import java.util.jar.JarFile |
| |
| class VdlPlugin implements Plugin<Project> { |
| void apply(Project project) { |
| project.extensions.create('vdl', VdlConfiguration) |
| |
| def extractTask = project.task('extractVdl', type: Copy) { |
| from { |
| getVdlToolJarFiles(project, project.buildscript.configurations.classpath) |
| } |
| into { new File(project.buildDir, 'vdltool') } |
| } |
| def clearVdlOutputTask = project.task('clearVdlOutput', type: Delete) { |
| delete { project.vdl.outputPath } |
| delete { project.vdl.transitiveVdlDir } |
| } |
| def generateTask = project.task('generateVdl', type: Exec) { |
| } |
| def vdlTask = project.task('vdl') { |
| group = "Build" |
| description('Generates Java vdl source using the vdl tool') |
| } |
| def prepareTask = project.task('prepareVdl') { |
| doLast { |
| List<String> vdlPaths = extractTransitiveVdlFilesAndGetInputPaths(project).asList() |
| List<String> outDirs = getJavaOutDirs(project) |
| |
| generateTask.environment(VDLROOT: getVdlRootPath(project)) |
| generateTask.environment(VDLPATH: vdlPaths.join(":")) |
| List<String> commandLine = ['build/vdltool/vdl-' + getOsName(), |
| 'generate', |
| '--lang=java', |
| '--java-out-dir=' + outDirs.join(",") |
| ] |
| if (!project.vdl.packageTranslations.isEmpty()) { |
| commandLine.add('--java-out-pkg=' + project.vdl.packageTranslations.join(',')) |
| } |
| commandLine.add('all') |
| generateTask.commandLine(commandLine) |
| } |
| } |
| def removeVdlRootTask = project.task('removeVdlRoot', type: Delete) { |
| onlyIf { !project.vdl.generateVdlRoot } |
| delete { project.vdl.outputPath + '/io/v/v23/vdlroot/' } |
| } |
| extractTask.dependsOn(prepareTask) |
| generateTask.dependsOn(clearVdlOutputTask) |
| generateTask.dependsOn(extractTask) |
| removeVdlRootTask.dependsOn(generateTask) |
| vdlTask.dependsOn(removeVdlRootTask) |
| |
| if (project.vdl.formatGeneratedVdl) { |
| def formatTask = project.task('formatGeneratedVdlFiles', type: FormatTask) { |
| format project.fileTree({ project.vdl.outputPath }).include('**/*.java') |
| } |
| formatTask.dependsOn(removeVdlRootTask) |
| vdlTask.dependsOn(formatTask) |
| } |
| if (project.plugins.hasPlugin('com.android.library') |
| || project.plugins.hasPlugin('com.android.application')) { |
| project.tasks.'preBuild'.dependsOn(vdlTask) |
| project.android.sourceSets.main.java.srcDir({ project.vdl.outputPath }) |
| } |
| |
| if (project.plugins.hasPlugin('java')) { |
| project.compileJava.dependsOn(vdlTask) |
| project.sourceSets.main.java.srcDir({ project.vdl.outputPath }) |
| } |
| |
| if (project.hasProperty('clean')) { |
| project.clean.delete({ project.vdl.outputPath }) |
| project.clean.delete({ project.vdl.transitiveVdlDir }) |
| } |
| |
| project.afterEvaluate({ |
| if (project.plugins.hasPlugin('java')) { |
| // Add VDL files in VDL input paths to project resources. |
| project.vdl.inputPaths.each { |
| project.sourceSets.main.resources.srcDirs(it).include('**/*.vdl') |
| } |
| |
| // Ensure that empty directories are not copied. |
| project.tasks.withType(ProcessResources, { |
| it.setIncludeEmptyDirs(false) |
| }) |
| } |
| }) |
| } |
| |
| public static String getVdlRootPath(Project project) { |
| if (null == project.vdl.vdlRootPath || "".equals(project.vdl.vdlRootPath)) { |
| // Look in the transitive VDL files, it should be there so long as the project depends |
| // on VDL. |
| File candidateVdlRoot = new File( |
| new File(project.getProjectDir(), project.vdl.transitiveVdlDir), 'v.io/v23/vdlroot') |
| if (candidateVdlRoot.exists() && candidateVdlRoot.isDirectory()) { |
| return candidateVdlRoot.getPath() |
| } |
| |
| throw new InvalidUserDataException('could not determine a value for VDLROOT, ' |
| + 'you should check to ensure your project depends on vanadium') |
| } else { |
| return project.vdl.vdlRootPath |
| } |
| } |
| |
| public static Set<String> extractTransitiveVdlFilesAndGetInputPaths(Project project) { |
| Set<String> result = new LinkedHashSet<>(project.vdl.inputPaths) |
| // Go through the dependencies of all configurations looking for jar files containing |
| // VDL files. |
| project.configurations.each({ |
| it.each({ |
| if (it.getName().endsWith('.jar') && it.exists()) { |
| if (extractVdlFiles(it, new File(project.getProjectDir(), project.vdl.transitiveVdlDir))) { |
| result.add(project.vdl.transitiveVdlDir) |
| } |
| } |
| }) |
| }) |
| |
| // Now recursively descend through the project's project dependencies looking for |
| // VDL projects. |
| Set<String> projectInputPaths = new LinkedHashSet<>() |
| addVdlInputPathsForProject(project, projectInputPaths) |
| // Copy any VDL files in any input path to the transitive VDL directory. |
| project.copy { |
| projectInputPaths.each({ |
| from(it) { |
| include '**/*.vdl' |
| includeEmptyDirs = false |
| } |
| }) |
| |
| into project.vdl.transitiveVdlDir |
| } |
| if (!projectInputPaths.isEmpty()) { |
| result.add(project.vdl.transitiveVdlDir) |
| } |
| return result |
| } |
| |
| public static List<String> getJavaOutDirs(Project project) { |
| List<String> result = new ArrayList<>() |
| for (String inputPath : project.vdl.inputPaths) { |
| if (!inputPath.startsWith(project.vdl.transitiveVdlDir)) { |
| result.add(inputPath + '->' + project.vdl.outputPath) |
| } |
| } |
| result.add(project.vdl.transitiveVdlDir + "->" + project.vdl.transitiveVdlDir) |
| return result |
| } |
| |
| /** |
| * Extracts any vdl files in the given jar file to the given destination directory. Returns |
| * {@code true} if any files were extracted. |
| */ |
| private static boolean extractVdlFiles(File jarFile, File destination) { |
| JarFile jar = new JarFile(jarFile) |
| Enumeration<JarEntry> entries = jar.entries() |
| boolean extracted = false |
| while (entries.hasMoreElements()) { |
| JarEntry entry = entries.nextElement() |
| if (entry.isDirectory() || !entry.getName().endsWith('.vdl')) { |
| continue |
| } |
| // Create the file's directory. |
| File destinationFile = new File(destination, entry.getName()) |
| destinationFile.getParentFile().mkdirs() |
| InputStream input = jar.getInputStream(entry) |
| OutputStream output = new FileOutputStream(destinationFile) |
| copyStream(input, output) |
| extracted = true |
| input.close() |
| output.close() |
| } |
| return extracted |
| } |
| |
| private static void copyStream(InputStream source, OutputStream destination) { |
| byte[] buffer = new byte[1 << 16] |
| while (true) { |
| int r = source.read(buffer) |
| if (r == -1) { |
| return |
| } |
| destination.write(buffer, 0, r) |
| } |
| } |
| |
| /** |
| * Returns a list of {@link java.io.File} instances representing VDL binaries. |
| */ |
| static List<File> getVdlToolJarFiles(Project project, FileCollection files) { |
| List<File> result = new ArrayList<File>() |
| |
| for (File file : files.findAll({ |
| it.name.contains 'gradle-plugin' |
| })) { |
| project.zipTree(file).findAll({ it.name.startsWith("vdl-") }).each({ |
| result.add(project.resources.text.fromArchiveEntry(file, it.name).asFile()) |
| }) |
| } |
| |
| return result |
| } |
| |
| private static void addVdlInputPathsForProject(Project project, Set<String> inputPaths) { |
| if (project == null) { |
| return |
| } |
| Object extension = project.extensions.findByName('vdl') |
| if (extension != null) { |
| extension.inputPaths.each { |
| File newPath = new File(it) |
| if (!newPath.isAbsolute()) { |
| newPath = new File(project.getProjectDir(), it) |
| } |
| inputPaths.add(newPath.getAbsolutePath()) |
| } |
| } |
| project.configurations.each({ |
| it.dependencies.each({ |
| if (it instanceof ProjectDependency) { |
| addVdlInputPathsForProject(it.getDependencyProject(), inputPaths) |
| } |
| }) |
| }) |
| } |
| |
| private static isMacOsX() { |
| return System.properties['os.name'].toLowerCase().contains("os x") |
| } |
| |
| private static isLinux() { |
| return System.properties['os.name'].toLowerCase().contains("linux") |
| } |
| |
| static String getOsName() { |
| if (isLinux()) { |
| return "linux"; |
| } else if (isMacOsX()) { |
| return "macosx"; |
| } else { |
| throw new IllegalStateException("Unsupported operating system " + System.properties.'os.name') |
| } |
| } |
| } |
| |
| class VdlConfiguration { |
| List<String> inputPaths = [] |
| String vdlRootPath = "" |
| String outputPath = "generated-src/vdl" |
| String transitiveVdlDir = "generated-src/transitive-vdl" |
| String vdlToolPath = "" |
| List<String> packageTranslations = ["v.io->io/v"] |
| boolean formatGeneratedVdl = true |
| |
| // If true, code generated for the vdlroot vdl package will be emitted. |
| // Typically, users will want to leave this set to false as they will |
| // already get the vdlroot package by depending on the :lib project. |
| boolean generateVdlRoot = false; |
| } |
| |