mirror of https://github.com/jruby-gradle/jem
182 lines
6.4 KiB
Groovy
182 lines
6.4 KiB
Groovy
package com.github.jrubygradle.groovygem.internal
|
|
|
|
import org.apache.commons.io.IOUtils
|
|
import org.apache.commons.vfs2.AllFileSelector
|
|
import org.apache.commons.vfs2.FileFilter
|
|
import org.apache.commons.vfs2.FileFilterSelector
|
|
import org.apache.commons.vfs2.FileNotFolderException
|
|
import org.apache.commons.vfs2.FileObject
|
|
import org.apache.commons.vfs2.FileSelectInfo
|
|
import org.apache.commons.vfs2.FileSystemManager
|
|
import org.apache.commons.vfs2.VFS
|
|
import org.slf4j.Logger
|
|
import org.slf4j.LoggerFactory
|
|
|
|
import groovy.transform.CompileStatic
|
|
|
|
import com.github.jrubygradle.groovygem.Gem
|
|
import com.github.jrubygradle.groovygem.GemInstaller.DuplicateBehavior
|
|
|
|
import java.nio.file.Files
|
|
|
|
@CompileStatic
|
|
class GemInstaller {
|
|
static final List<String> GEM_HOME_DIRS = ['bin', 'build_info', 'cache', 'doc',
|
|
'extensions', 'gems', 'specifications']
|
|
|
|
protected Logger logger = LoggerFactory.getLogger(this.class)
|
|
protected File installDirectory
|
|
protected List<File> gems
|
|
protected FileSystemManager fileSystemManager
|
|
|
|
GemInstaller(String installDir, List<File> gems) {
|
|
this.installDirectory = new File(installDir)
|
|
this.gems = gems
|
|
this.fileSystemManager = VFS.manager
|
|
}
|
|
|
|
/** Install and overwrite anything that stands in the way */
|
|
void install() {
|
|
install(DuplicateBehavior.OVERWRITE)
|
|
}
|
|
|
|
void install(DuplicateBehavior onDuplicateBehavior) {
|
|
if (!mkdirs()) {
|
|
/* raise some exception? */
|
|
}
|
|
|
|
gems.each { File gem ->
|
|
installGem(installDirectory, gem, onDuplicateBehavior)
|
|
}
|
|
}
|
|
|
|
boolean installGem(File installDir, File gem, DuplicateBehavior onDuplicate) {
|
|
/* TODO: isValidGem? */
|
|
cacheGemInInstallDir(installDir, gem)
|
|
|
|
FileObject gemTar = fileSystemManager.resolveFile("tar:${gem}")
|
|
FileObject tempTar = fileSystemManager.resolveFile("ram://${gem.name}-data.tar.gz")
|
|
/* http://wiki.apache.org/commons/ExtractAndDecompressGzipFiles */
|
|
FileObject metadata = fileSystemManager.resolveFile("gz:tar:${gem}!/metadata.gz!metadata")
|
|
|
|
Gem gemMetadata = Gem.fromFile(metadata.content.inputStream)
|
|
logger.info("We've processed metadata for ${gemMetadata.name} at version ${gemMetadata.version}")
|
|
metadata.content.close()
|
|
|
|
long size = gemTar.getChild('data.tar.gz').content.write(tempTar)
|
|
logger.info("Extracted data.tar.gz from ${gem} (${size} bytes)")
|
|
|
|
FileObject dataTar = fileSystemManager.resolveFile("tgz:${tempTar}")
|
|
logger.info("The contents of our data.tar.gz: ${dataTar.children}")
|
|
|
|
extractSpecification(installDir, gemMetadata)
|
|
extractData(installDir, dataTar, gemMetadata)
|
|
extractExecutables(installDir, dataTar, gemMetadata)
|
|
|
|
gemTar.close()
|
|
metadata.close()
|
|
tempTar.delete()
|
|
dataTar.delete()
|
|
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Create the requisite directories to map a GEM_HOME structure, namely:
|
|
* bin/
|
|
* build_info/
|
|
* cache/
|
|
* doc/
|
|
* extensions/
|
|
* gems/
|
|
* specifications/
|
|
*
|
|
* @return True if all directories were created successfully
|
|
*/
|
|
boolean mkdirs() {
|
|
boolean success = true
|
|
GEM_HOME_DIRS.each { String dirName ->
|
|
File newDir = new File(installDirectory, dirName)
|
|
logger.info("Attempting to create: ${newDir.absolutePath}")
|
|
|
|
if (!newDir.mkdirs()) {
|
|
logger.error("Failed to make ${newDir.absolutePath}, bailing on mkdirs()")
|
|
return false
|
|
}
|
|
}
|
|
return success
|
|
}
|
|
|
|
/**
|
|
* Primarily meant to be an internal method which will determine whether the
|
|
* given {@code java.io.File} is a valid gem archive or not. This includes looking
|
|
* inside it to see that it is a legitimate tar file
|
|
*
|
|
* @param gemFile Fully formed {@code java.io.File} object referencing a gem
|
|
* @return true if the file does in fact walk and talk like a gem
|
|
*/
|
|
boolean isValidGem(File gemFile) {
|
|
logger.info("Validating gem ${gemFile}")
|
|
|
|
/* If it doesn't end with gem, let's not even consider it a gem file */
|
|
if (!gemFile.absolutePath.endsWith('.gem')) {
|
|
return false
|
|
}
|
|
|
|
FileObject fo = fileSystemManager.resolveFile("tar:${gemFile}")
|
|
|
|
try {
|
|
return fo.children.size() > 0
|
|
}
|
|
catch (FileNotFolderException ex) {
|
|
/*
|
|
* if we've received this exception its because the gem file, aka
|
|
* tar file isn't actually legit
|
|
*/
|
|
return false
|
|
}
|
|
}
|
|
|
|
/** Cache the gem in GEM_HOME/cache */
|
|
protected void cacheGemInInstallDir(File installDir, File gem) {
|
|
File cacheDir = new File(installDir, 'cache')
|
|
Files.copy(gem.toPath(), (new File(cacheDir, gem.name)).toPath())
|
|
}
|
|
|
|
/** Extract the gemspec file from the {@code Gem} provided into the ${installDir}/specifications */
|
|
protected void extractSpecification(File installDir, Gem gem) {
|
|
String fileName = "${gem.name}-${gem.version.version}"
|
|
if (gem.platform != 'ruby') {
|
|
fileName = "${fileName}-${gem.platform}"
|
|
}
|
|
String outputFileName = "${fileName}.gemspec"
|
|
FileObject outputFile = fileSystemManager.resolveFile(new File(installDir, 'specifications'), outputFileName)
|
|
|
|
PrintWriter writer = new PrintWriter(outputFile.content.outputStream)
|
|
writer.write(gem.toRuby())
|
|
writer.flush()
|
|
outputFile.content.close()
|
|
}
|
|
|
|
protected void extractData(File installDir, FileObject dataTarGz, Gem gem) {
|
|
String dir = "${gem.name}-${gem.version.version}"
|
|
if (gem.platform != 'ruby') {
|
|
dir = "${dir}-${gem.platform}"
|
|
}
|
|
logger.info("Extracting into ${installDir} from ${gem.name}")
|
|
FileObject outputDir = fileSystemManager.resolveFile(new File(installDir, 'gems'), dir)
|
|
outputDir.copyFrom(dataTarGz, new AllFileSelector())
|
|
outputDir.close()
|
|
}
|
|
|
|
protected void extractExecutables(File installDir, FileObject dataTarGz, Gem gem) {
|
|
String binDir = gem.bindir ?: 'bin'
|
|
FileObject binObject = fileSystemManager.resolveFile(installDir, 'bin')
|
|
FileObject child = dataTarGz.getChild(binDir)
|
|
if (!child) {
|
|
return
|
|
}
|
|
binObject.copyFrom(child, new AllFileSelector())
|
|
binObject.close()
|
|
}
|
|
} |