Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Rework plugin structure to replace pluggable with a xml file #91

Merged
merged 11 commits into from
Jul 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pluginTargetFolderNames := List("plugins", s"target/scala-$scalaMajorVersion/plu
apiProjectPath := "api"
guiProjectPath := "gui"

create := BuildUtility(streams.value.log).createPluginTask(pluginFolderNames.value)
create := PluginCreateWizard(streams.value.log).createPluginTask(pluginFolderNames.value)
fetch := BuildUtility(streams.value.log).fetchPluginsTask(pluginFolderNames.value, pluginBuildFileName.value,
pluginTargetFolderNames.value, apiProjectPath.value)
copy := BuildUtility(streams.value.log).copyPluginsTask(pluginFolderNames.value, pluginTargetFolderNames.value, scalaMajorVersion)
Expand Down
103 changes: 6 additions & 97 deletions project/BuildUtility.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,86 +131,6 @@ class BuildUtility(logger: ManagedLogger) {
logger info s"Successfully copied $successCounter / ${allJarFiles.length} plugins to target '${pluginTargetFolder.getPath}'!"
}

/**
* Creates a new plugin. Interactive command using the console.
*
* @param pluginFolderNames All folder names, containing plugin source code. Defined in build.sbt.
*/
def createPluginTask(pluginFolderNames: List[String]): Unit = {
withTaskInfo("CREATE PLUGIN") {

// Plugin folders have to be defined in the build.sbt file first
if (pluginFolderNames.isEmpty) {
println("Before creating a new plugin, please define at least one plugin source folder in the build.sbt file.")
logger warn "Aborting task without plugin creation."

} else {
println("Welcome to the \"create plugin\"-wizard. Please specify name, version and plugin source folder.")

// Plugin name
val name = BuildUtility.askForInput(
"Please specify the name of the plugin. Do only use characters allowed for directories and files of your OS.",
"Plugin name",
repeatIfEmpty = true
)

// Plugin version (default: 0.1)
var version = BuildUtility.askForInput(
"Please specify the version of the plugin. Just press enter for version \"0.1\".",
"Plugin version",
repeatIfEmpty = false
)
if (version == "") version = "0.1"

// Plugin folder name (must be defined in build.sbt)
var pluginFolderName = ""
while (!pluginFolderNames.contains(pluginFolderName)) {
pluginFolderName = BuildUtility.askForInput(
s"Please specify the plugin source directory. Available directories: ${pluginFolderNames.mkString("[", ", ", "]")}",
"Plugin source directory",
repeatIfEmpty = true
)
}

createPlugin(name, version, pluginFolderName)
}
}
}

private def withTaskInfo(taskName: String)(task: Unit): Unit = BuildUtility.withTaskInfo(taskName, logger)(task)

private def createPlugin(name: String, version: String, pluginFolderName: String): Unit = {
logger info s"Trying to create plugin $name (version $version) at plugin folder $pluginFolderName."

val pluginFolder = new File(pluginFolderName)
if (!pluginFolder.exists()) {
logger error "Plugin source folder does not exist. Aborting task without plugin creation."

} else {

val plugin = new Plugin(pluginFolderName, name)

if (!plugin.createPluginFolder()) {
logger error "Plugin does already exist. Aborting task without plugin creation."
} else {
logger info s"Created plugin '$name'"

if (plugin.createSrcFolder()) {
logger info "Successfully created source folder."
} else {
logger warn "Unable to create source folder."
}

if (plugin.createSbtFile(version)) {
logger info "Successfully created plugins sbt file."
} else {
logger warn "Unable to create plugins sbt file."
}

}
}
}

def guiTask(guiProjectPath: String, cacheDir: File): Unit = {
withTaskInfo("BUILD GUI") {
val guiDir = new File(guiProjectPath)
Expand All @@ -234,13 +154,13 @@ class BuildUtility(logger: ManagedLogger) {
/**
* Download the dependencies of the gui using npm.
*
* @param guiDir the directory of the gui.
* @param guiDir the directory of the gui.
* @param cacheDir a dir, where sbt can store files for caching in the "install" sub-dir.
* @return None, if a error occurs which will be displayed, otherwise the output directory with the built gui.
*/
private def installGuiDeps(guiDir: File, cacheDir: File): Option[File] = {
// Check buildGui for a explanation, it's almost the same.

val install = FileFunction.cached(new File(cacheDir, "install"), FilesInfo.hash)(_ => {

logger info "Installing GUI dependencies."
Expand All @@ -266,8 +186,8 @@ class BuildUtility(logger: ManagedLogger) {

/**
* Builds the gui using npm.
*
* @param guiDir the directory of the gui.
*
* @param guiDir the directory of the gui.
* @param cacheDir a dir, where sbt can store files for caching in the "build" sub-dir.
* @return None, if a error occurs which will be displayed, otherwise the output directory with the built gui.
*/
Expand Down Expand Up @@ -326,25 +246,14 @@ class BuildUtility(logger: ManagedLogger) {
Set(f)
}
}

private def withTaskInfo(taskName: String)(task: Unit): Unit = BuildUtility.withTaskInfo(taskName, logger)(task)
}

object BuildUtility {

def apply(logger: ManagedLogger): BuildUtility = new BuildUtility(logger)

private def askForInput(information: String, description: String, repeatIfEmpty: Boolean): String = {
println(information)
print(s"$description > ")

var input = scala.io.Source.fromInputStream(System.in).bufferedReader().readLine()
println("")

if (input == "" && repeatIfEmpty)
input = askForInput(information, description, repeatIfEmpty)

input
}

/**
* This method can be used to create better readable sbt console output by declaring start and stop of a custom task.
*
Expand Down
78 changes: 59 additions & 19 deletions project/Plugin.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import java.io.File

import sbt.io.IO

import scala.util.Try
import scala.xml.PrettyPrinter

/**
* A plugin represents a directory in a plugin source directory. Every plugin has its own build file and source folder.
*
Expand All @@ -26,22 +31,6 @@ class Plugin(val pluginSourceDirectoryName: String, val name: String) {
}
}

/**
* Creates the plugin src folder inside of a plugin folder
*
* @note Make sure to create the plugin folder first!
* @return true, if the process was successful
*/
def createSrcFolder(): Boolean = {
if (new File(s"$pluginDirectoryPath/src").mkdir() &&
new File(s"$pluginDirectoryPath/src/main").mkdir() &&
new File(s"$pluginDirectoryPath/src/main/scala").mkdir()) {
true
} else {
false
}
}

/**
* Creates a simple sbt file with name and version info into the plugin folder
*
Expand All @@ -52,7 +41,53 @@ class Plugin(val pluginSourceDirectoryName: String, val name: String) {
val sbtFile = new SbtFile(name, version)

// The name of the sbt file is the plugin name. This worked in first tests
sbtFile.save(s"$pluginDirectoryPath/$name.sbt")
sbtFile.save(s"$pluginDirectoryPath/$normalizedName.sbt")
}

/**
* Generates the plugin.xml file in the resources of the plugin.
*
* @param metadata the metadata for this plugin
* @param author author of this plugin, used by the framework to identify it
*/
def createPluginXMLFile(metadata: PluginMetadata, author: String, version: String, apiVersion: (Int, Int)): Boolean = {
val xml = <plugin>
<name>
{name}
</name>
<author>
{author}
</author>
<version>
{version}
</version>
<api>
<major>{apiVersion._1}</major>
<minor>{apiVersion._2}</minor>
</api>{metadata.toXML}
</plugin>

val trimmed = scala.xml.Utility.trim(xml)
val prettyXml = new PrettyPrinter(100, 2).format(trimmed)

Try(
IO.write(new File(s"$pluginDirectoryPath/src/main/resources/plugin.xml"), prettyXml)
).isSuccess
}

/**
* Generates the main class file implementing PluginImpl for the plugin developer in their choosen language.
*
* @param language the language in with the source file will be generated
* @return true, if everything was successful
*/
def createSourceFile(language: PluginLanguage.Value): Boolean = {
val content = PluginLanguage.getSourceFileContent(normalizedName, language)
val langName = language.toString.toLowerCase

Try(
IO.write(new File(s"$pluginDirectoryPath/src/main/$langName/${normalizedName}Plugin.$langName"), content.getBytes)
).isSuccess
}

/**
Expand Down Expand Up @@ -94,7 +129,9 @@ object Plugin {
*/
def getPlugins(pluginSourceFolderName: String): Seq[Plugin] = {
val pluginSourceFolder = new File(pluginSourceFolderName)
pluginSourceFolder.listFiles.filter(_.isDirectory).filter(d => d.getName != ".git" && d.getName != ".github")
pluginSourceFolder.listFiles
.filter(_.isDirectory)
.filter(d => containsPluginXMLFile(d))
.map(folder => new Plugin(pluginSourceFolderName, folder.getName))

}
Expand All @@ -110,8 +147,11 @@ object Plugin {
pluginSourceFolder.exists() && pluginSourceFolder.isDirectory
}

private def toPluginPathName(name: String) = name.replace(" ", "").toLowerCase
private def toPluginPathName(name: String) = name.replaceAll("[ -]", "").toLowerCase

private def containsPluginXMLFile(directory: File): Boolean = {
new File(s"$directory/src/main/resources/plugin.xml").exists()
}
}


Loading