Skip to content

Commit

Permalink
Finished porting the Platform to use the XTC plugin. Rewrite some of …
Browse files Browse the repository at this point in the history
…the documentation
  • Loading branch information
lagergren committed Dec 20, 2023
1 parent e7a7a30 commit 80f2b9d
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 129 deletions.
75 changes: 57 additions & 18 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,54 +1,93 @@
/*
/**
* Main build file for the "platform" project.
*/

/**
* Plugins required:
*
* Enable the XTC plugin, so that we can parse this build file. In the interest to avoid
* hardcoded artifact descriptors, and copy-and-paste for versions, we refer to the
* plugin aliases declared in "gradle/libs.versions.toml"
*/
plugins {
alias(libs.plugins.xtc)
alias(libs.plugins.tasktree) // for debugging purposes, for example:
}

/**
* Dependencies to other projects, configurations and artifacts.
*
* These are the dependencies to other projects, and to the XDK proper (versioned). We follow
* the Gradle Version Catalog standard for this project, and normally, when changing the version
* of any requested artifact or plugin, there should only be the need to change
* "gradle/libs.versions.toml"
*/
dependencies {
xdkZip(libs.xdk)
xtcModule(project(":kernel"))
xtcModule(project(":kernel")) // main module to run.
xtcModule(project(":common")) // runtime path
xtcModule(project(":platformDB")) // runtime path
xtcModule(project(":host")) // runtime path
xtcModule(project(":platformDB")) // runtime path
xtcModule(project(":platformUI")) // runtime path
}

//x/ec -L lib/ kernel.xtc [password]")
internal val mainModuleName = "kernel"

/*
* xtcRun is a configuration for anything launching from the main source set.
* xtcRunTest
/**
* This is the run configuration, which configures all xtcRun taks for the main source set. (runXtc, runAllXtc)
* The DSL for modules to run is a list of "module { }" elements or a list of moduleName("...") statements.
* To look at the DSL for all parts of the XTC build, you can use your IDE and browse the implementing
* classes. For example, there should be a hint in IntelliJ with the type for the xtcRun element and
* the modules element (DefaultXtcRuntimeExtension and XtcRuntimeExtension.XtcRunModule, respectively).
* It is a good way to understand how the build DSL works, so you can add your own powerful XTC build
* syntax constructs and nice syntactic sugar/shorthand for things you feel should be simpler to write.
*/
xtcRun {
verbose = true
module {
moduleName = "kernel"
// TOOD: Check that grabbing properties through -P or -D or System.getenv works and maps to XTC properties.
// TODO: Implement arg() and args() syntactic sugar
args = listOf(readPassword())
moduleName = mainModuleName
args(readPassword().get()) // TODO: Implement a version of args that takes a provider (Object...) so we can evaluate the password just as we are about to use it.
// methodName = "run" // This is default, and we don't need to specify it.
}
}

val runXtc by tasks.existing {
// Add a dependency on the build to ensure that any subcomponents that have "build" tasks, but may not have them
// due to being proper Gradle lifecycle projects, are built. I am 99% sure we can just remove this.
dependsOn(tasks.build)
}

val run by tasks.registering {
group = "application"
description = "Build (if necessary) and run the platform (equivalent to 'xec [-L <module>]+ kernel.xtc <password>)"
dependsOn("runXtc")
dependsOn(tasks.runXtc)
doFirst {
logger.lifecycle("Starting the XTC platform (kernel).")
}
}

internal fun readPassword(): String {
val password = findProperty("org.xtclang.platform.password")?.toString() ?: ""
/**
* Read the password. Typically, the password is either placed as a Gradle property
* with the key "org.xtclang.kernel.password" in an external gradle.properties or init
* file outside of the project. The most common choice is $GRADLE_USER_HOME/.gradle.properties,
* which generatally contains secrets.
*
* You can also send values as project properties for the root project by using the
* "-P" switch on the Gradle command line, like so:
* "./gradlew run -Porg.xtclang.kernel.password=Uhlers0th"
*
* Here, we have also added a final way of passing the password property through a Java
* System property, which is equivalent to the above, but would use the "-D" switch instead
* of the "-P" switch, and would make the property value available to any project under
* the same "gradlew" run, just like for a Java program. For example:
* "./gradlew run -Dorg.xtclang.kernel.password=Uhlers0th"
*/
internal fun readPassword(): Provider<String> = provider {
val key = "org.xtclang.${project.name}.password"
val password = findProperty(key)?.toString() ?: System.getProperty(key) ?: ""
if (password.isEmpty()) {
throw GradleException("Error. No password was given.")
throw GradleException("Error. No password was found for key: '$key'.")
}
logger.lifecycle("Resolved password: [REDACTED]")
return password
}
logger.lifecycle("Successfully resolved password: '$key' -> [REDACTED]")
password
}
35 changes: 0 additions & 35 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,3 @@ plugins {
dependencies {
xdkZip(libs.xdk)
}

xtcCompile {
verbose = true
}

// Rather than forcing the XTC module to show up somewhere, it should be an outgoing configuration or
// an artifact.
/*
-val libDir = "${rootProject.projectDir}/lib"
+plugins {
+ alias(libs.plugins.xtc)
+}
-tasks.register("build") {
- group = "Build"
- description = "Build this module"
+dependencies {
+ xdkZip(libs.xdk)
+}
- val src = fileTree("${projectDir}/src").files.stream().
- mapToLong{f -> f.lastModified()}.max().orElse(0)
- val dst = file("$libDir/common.xtc").lastModified()
-
- if (src > dst) {
- val srcModule = "${projectDir}/src/main/x/common.x"
-
- project.exec {
- commandLine("xcc", "-verbose",
- "-o", libDir,
- srcModule)
- }
- }
-}
*/
14 changes: 13 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
#
# This is the only file in the project where we keep version information and artifact
# names for our plugins and dependencies.
#
# TODO: Note that there are still a few hardcoded artifact names nad versions in some places in the
# project, mostly on the settings level, due to boostrapping issues. These can be handled better, but
# we haven't had enough time to spend on it + it will also be significantly simpler, once we are up
# and running with artifact repositories for our Gradle artifacts + regularly publish both releases
# (and snapshot releases internally) to gradlePluginPortal, mavenCentral and the XTC Organization
# GitHub artifact repository)
#

[versions]
node = "7.0.1"
npm = "10.2.0"
Expand All @@ -10,7 +22,7 @@ node = { id = "com.github.node-gradle.node", version.ref = "node" }
xtc = { id = "org.xtclang.xtc-plugin", version.ref = "xdk" }

# taskTree is a helper that we can use to view task dependencies
# for example: ./gradlew run taskTree
# for example: ./gradlew run taskTree, or ./gradle run taskTree --with-inputs --with-outputs
tasktree = { id = "com.dorongold.task-tree", version.ref = "tasktree" }

[libraries]
Expand Down
30 changes: 0 additions & 30 deletions host/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
* Build the host module.
*/

// Depends on common project being built, and put its xtc modules on the path.

plugins {
alias(libs.plugins.xtc)
}
Expand All @@ -12,31 +10,3 @@ dependencies {
xdkZip(libs.xdk)
xtcModule(project(":common"))
}

/*
val libDir = "${rootProject.projectDir}/lib"
tasks.register("build") {
group = "Build"
description = "Build this module"
dependsOn(project(":common").tasks["build"])
doLast {
val src = fileTree("${projectDir}/src").files.stream().
mapToLong{f -> f.lastModified()}.max().orElse(0)
val dst = file("$libDir/host.xtc").lastModified()
if (src > dst) {
val srcModule = "${projectDir}/src/main/x/host.x"
project.exec {
commandLine("xcc", "-verbose",
"-o", libDir,
"-L", libDir,
srcModule)
}
}
}
}
*/
29 changes: 0 additions & 29 deletions kernel/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,3 @@ dependencies {
xtcCompile {
verbose = true
}

/*
val libDir = "${rootProject.projectDir}/lib"
tasks.register("build") {
group = "Build"
description = "Build this module"
dependsOn(project(":common") .tasks["build"])
dependsOn(project(":platformDB") .tasks["build"])
doLast {
val src = fileTree("${projectDir}/src").files.stream().
mapToLong{f -> f.lastModified()}.max().orElse(0)
val dst = file("$libDir/kernel.xtc").lastModified()
if (src > dst) {
val srcModule = "${projectDir}/src/main/x/kernel.x"
project.exec {
commandLine("xcc", "-verbose",
"-o", libDir,
"-L", libDir,
srcModule)
}
}
}
}*/
8 changes: 4 additions & 4 deletions platformDB/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* The platform database subproject.
*/

plugins {
alias(libs.plugins.xtc)
}
Expand All @@ -6,7 +10,3 @@ dependencies {
xdkZip(libs.xdk)
xtcModule(project(":common"))
}

xtcCompile {
verbose = true
}
47 changes: 40 additions & 7 deletions platformUI/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
/**
* The platform UI subproject. This relies on npm and yarn for building the web
* UI. We plug the node builder and the quasar runner into the Gradle lifecycle,
* so that we don't need to rebuild the non-XTC parts of the webapp unless something
* has explicitly changed.
*
* We also use the version catalog to resolve the name and version of the popular
* third party Node plugin for Gradle.
*
* This project used to be buildable both with Npm and Yarn, but due to time
* constraints, reimplementing the Npm functionality is in the backlog. The user
* should not need to care anymore, however, because the build system takes care
* of setting up the web app frameworks, and make sure they interact correctly
* with the rest of the Gradle build lifecycle.
*/

import com.github.gradle.node.yarn.task.YarnTask

node {
version = libs.versions.node.get()
npmVersion = libs.versions.npm.get()
yarnVersion = libs.versions.yarn.get()

// TODO: Typically you would set download=true here to ensure that we download and use
// exactly the versions of the tools above. These would go into the .gradle cache
// directory of this subproject, and not anywhere else in the system. However,
Expand All @@ -28,15 +45,21 @@ dependencies {
xtcModule(project(":common"))
}

// TODO: Run a build scan and figure out why yarn wants to invalidate dependencies.
// TODO: Implement a parallel NPM / package-lock based approach. Yarn does not like having a package lock in the same build.
// TODO: Future webapp improvement; implement a parallel NPM / package-lock based approach. Yarn does not like having a package lock in the same build.
internal val gui = project.file("gui")
internal val buildDirs = arrayOf("gui/node_modules", "gui_dist", "gui/.quasar")

val compileXtc by tasks.existing {
dependsOn(yarnQuasarBuild)
}

/**
* By adding the gui/dist folder as a resource directory, the build will also treat
* it like an input to the build result. This means that any changes of its contents
* or timestamps will require that we rebuild it and its dependencies. This also means
* that as long as it stays unchanged, a finished build task for this project remains
* a no-op.
*/
sourceSets.main {
xtc {
resources {
Expand All @@ -49,11 +72,16 @@ val clean by tasks.existing {
delete(layout.files(*buildDirs))
}

// The yarnSetup task is just referenced here for type safety. We can also refer to it by name below,
// instead of to the variable "yarnSetup", but that is more brittle, and if we rename the task, any references
// to it might accidentally be forgotten.
val yarnSetup by tasks.existing

val yarnAddQuasar by tasks.registering(YarnTask::class) {
dependsOn(yarnSetup)
workingDir = gui
// Tag this task as a producer of the "node_modules" directory, implicitly ensuring that any changes
// to the resolved node_modules will make its dependents rebuild properly.
outputs.file("node_modules")
val isQuasarGlobal = (findProperty("org.xtclang.platform.quasarGlobal")?.toString() ?: "false").toBoolean()
args = buildList {
Expand All @@ -67,7 +95,7 @@ val yarnAddQuasar by tasks.registering(YarnTask::class) {

doFirst {
logger.lifecycle("Task '$name' installing Quasar (${if (isQuasarGlobal) "globally" else "locally, only for ${rootProject.name}"}.")
printTaskOutputs()
printTaskOutputs(LogLevel.INFO)
}
}

Expand All @@ -77,11 +105,16 @@ val yarnQuasarBuild by tasks.registering(YarnTask::class) {
outputs.files()
args = listOf("--ignore-engines", "quasar", "build")
doLast {
printTaskOutputs()
printTaskOutputs(LogLevel.INFO)
}
}

internal fun Task.printTaskOutputs() {
logger.lifecycle("${project.name} Task '$name' finished.")
outputs.files.asFileTree.forEachIndexed { i, it -> logger.lifecycle("$name: ** '$name' task output $i: $it") }
internal fun Task.printTaskOutputs(level: LogLevel = LogLevel.LIFECYCLE) {
val outputFiles = outputs.files.asFileTree
val n = outputFiles.count()
logger.log(level, "${project.name} Task '$name' finished.")
logger.log(level, "${project.name} Outputs (count: $n):")
outputs.files.asFileTree.forEachIndexed {
i, it -> logger.log(level, "${project.name} '$name' output $i (of $n): $it")
}
}
Loading

0 comments on commit 80f2b9d

Please sign in to comment.