From 93fe2e47937b668166396e155821c7d450e3a590 Mon Sep 17 00:00:00 2001
From: Thomas Walker Lynch
Date: Fri, 4 Oct 2024 08:46:17 +0000
Subject: [PATCH] more code from from the GQL_to_Cypher build script
---
.../#Ariandne.groovy#} | 183 ++++++-
developer/deprecated/.githolder | 0
.../deprecated/Ariandne.groovy | 9 +-
.../dependency_graph_definition.groovy | 387 ++++++++++++++
developer/document/cycle_detection.html | 105 ++++
developer/document/dependency_graph.html | 137 +++++
developer/document/groovy.el | 19 +
.../document/unique_node_label_check.txt | 15 +
developer/executor/make.sh | 4 +
developer/executor/release | 22 +-
developer/executor/version | 4 +
developer/groovy/build | 81 ++-
...eGraph$_run_build_scripts_f_closure3.class | Bin 0 -> 3794 bytes
.../AriadneGraph$_wellformed_q_closure1.class | Bin 0 -> 1736 bytes
.../AriadneGraph$_wellformed_q_closure2.class | Bin 0 -> 2149 bytes
developer/groovyc/AriadneGraph.class | Bin 0 -> 10174 bytes
developer/groovyc/AriadneGraph.groovy | 472 ++++++++++++++++++
.../{deprecated => scratch_pad}/.gitignore | 0
document/directory_naming.txt | 165 ++++--
.../temporary => scratch_pad}/.gitignore | 0
temporary/.gitignore | 2 -
tester/test0/TestGraph.class | Bin 0 -> 1673 bytes
tester/test0/TestGraph.groovy | 14 +
tester/test0/env_test0 | 4 +
tester/test0/graph.groovy | 2 -
tester/test0/make.sh | 3 +
...eGraph$_run_build_scripts_f_closure3.class | Bin 0 -> 3794 bytes
.../AriadneGraph$_wellformed_q_closure1.class | Bin 0 -> 1736 bytes
.../AriadneGraph$_wellformed_q_closure2.class | Bin 0 -> 2149 bytes
user/Ariadne_0.1/AriadneGraph.class | Bin 0 -> 10174 bytes
user/Ariadne_0.1/AriadneGraph.groovy | 178 +++++++
user/Ariadne_0.1/build | 82 ++-
32 files changed, 1734 insertions(+), 154 deletions(-)
rename developer/{groovy/Ariadne.groovy => deprecated/#Ariandne.groovy#} (71%)
create mode 100644 developer/deprecated/.githolder
rename user/Ariadne_0.1/Ariadne.groovy => developer/deprecated/Ariandne.groovy (98%)
create mode 100644 developer/deprecated/dependency_graph_definition.groovy
create mode 100644 developer/document/cycle_detection.html
create mode 100644 developer/document/dependency_graph.html
create mode 100644 developer/document/groovy.el
create mode 100644 developer/document/unique_node_label_check.txt
create mode 100755 developer/executor/make.sh
create mode 100755 developer/executor/version
create mode 100644 developer/groovyc/AriadneGraph$_run_build_scripts_f_closure3.class
create mode 100644 developer/groovyc/AriadneGraph$_wellformed_q_closure1.class
create mode 100644 developer/groovyc/AriadneGraph$_wellformed_q_closure2.class
create mode 100644 developer/groovyc/AriadneGraph.class
create mode 100644 developer/groovyc/AriadneGraph.groovy
rename developer/{deprecated => scratch_pad}/.gitignore (100%)
rename {developer/temporary => scratch_pad}/.gitignore (100%)
delete mode 100644 temporary/.gitignore
create mode 100644 tester/test0/TestGraph.class
create mode 100644 tester/test0/TestGraph.groovy
delete mode 100644 tester/test0/graph.groovy
create mode 100755 tester/test0/make.sh
create mode 100644 user/Ariadne_0.1/AriadneGraph$_run_build_scripts_f_closure3.class
create mode 100644 user/Ariadne_0.1/AriadneGraph$_wellformed_q_closure1.class
create mode 100644 user/Ariadne_0.1/AriadneGraph$_wellformed_q_closure2.class
create mode 100644 user/Ariadne_0.1/AriadneGraph.class
create mode 100644 user/Ariadne_0.1/AriadneGraph.groovy
diff --git a/developer/groovy/Ariadne.groovy b/developer/deprecated/#Ariandne.groovy#
similarity index 71%
rename from developer/groovy/Ariadne.groovy
rename to developer/deprecated/#Ariandne.groovy#
index 6956705..6c0cf17 100644
--- a/developer/groovy/Ariadne.groovy
+++ b/developer/deprecated/#Ariandne.groovy#
@@ -1,3 +1,181 @@
+class AriadneGraph {
+
+ // Instance variables for graph data if needed
+ Map node_map = [:]
+ List node_f_list = []
+
+ // Constructor to accept a graph definition (node_map and node_f_list)
+ AriadneGraph( Map node_map ,List node_f_list ){
+ this.node_map = node_map ?: [:]
+ this.node_f_list = node_f_list ?: []
+ }
+
+ /*--------------------------------------------------------------------------------
+ File utility functions
+ */
+
+ static Map unpack_file_path( String file_fp ){
+ def file = new File( file_fp )
+
+ def parent_dp = file.getParent()
+ def file_fn = file.getName()
+ def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[ 0..file_fn.lastIndexOf('.') - 1 ] : file_fn
+ def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[ file_fn.lastIndexOf('.') + 1..-1 ] : ''
+
+ return [
+ parent_dp: parent_dp
+ ,file_fn: file_fn
+ ,file_fn_base: file_fn_base
+ ,file_fn_ext: file_fn_ext
+ ]
+ }
+
+ static boolean file_exists_q( String node_label ){
+ def node_path = Paths.get( node_label )
+ return Files.exists( node_path )
+ }
+
+ /*--------------------------------------------------------------------------------
+ Node type checks and marking
+ */
+
+ static Set all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
+ static Set persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
+
+ static boolean leaf_q( Map node ){
+ return node && node.type == 'leaf'
+ }
+
+ static boolean has_mark( Map node ){
+ return node?.mark?.isNotEmpty()
+ }
+
+ static void set_mark( Map node ,String mark ){
+ node.mark = node.mark ?: [] as Set
+ node.mark << mark
+ }
+
+ static void clear_mark( Map node ,String mark ){
+ node?.mark?.remove( mark )
+ }
+
+ static boolean marked_good_q( Map node ){
+ return node && node.mark && ( 'wellformed' in node.mark ) && !( 'cycle_member' in node.mark ) && !( 'build_failed' in node.mark )
+ }
+
+ /*--------------------------------------------------------------------------------
+ Well-formed Node Check
+ */
+
+ static Set all_form_error_set = [
+ 'no_node'
+ ,'node_must_have_label'
+ ,'label_must_be_string'
+ ,'node_must_have_type'
+ ,'bad_node_type'
+ ,'neighbor_value_must_be_list'
+ ,'neighbor_reference_must_be_string'
+ ,'mark_property_value_must_be_set'
+ ,'unregistered_mark'
+ ,'missing_required_build_code'
+ ,'leaf_given_neighbor_property'
+ ,'leaf_given_build_property'
+ ] as Set
+
+ static Set wellformed_q( Map node ){
+ def form_error_set = [] as Set
+
+ if( !node ){
+ form_error_set << 'no_node'
+ return form_error_set
+ }
+
+ if( !node.label )
+ form_error_set << 'node_must_have_label'
+ else if( !( node.label instanceof String ) )
+ form_error_set << 'label_must_be_string'
+
+ if( !node.type )
+ form_error_set << 'node_must_have_type'
+ else if( !( node.type instanceof String ) || !( node.type in all_node_type_set ) )
+ form_error_set << 'bad_node_type'
+
+ if( node.neighbor ){
+ if( !( node.neighbor instanceof List ) )
+ form_error_set << 'neighbor_value_must_be_list'
+ else if( !( node.neighbor.every { it instanceof String } ) )
+ form_error_set << 'neighbor_reference_must_be_string'
+ }
+
+ if( node.mark ){
+ if( !( node.mark instanceof Set ) )
+ form_error_set << 'mark_property_value_must_be_set'
+ else if( !( node.mark.every { it in persistent_node_mark_set } ) )
+ form_error_set << 'unregistered_mark'
+ }
+
+ if( node.type == 'path' && ( !node.build || !( node.build instanceof Closure ) ) )
+ form_error_set << 'missing_required_build_code'
+
+ if( node.type == 'leaf' ){
+ if( node.neighbor ) form_error_set << 'leaf_given_neighbor_property'
+ if( node.build ) form_error_set << 'leaf_given_build_property'
+ }
+
+ return form_error_set
+ }
+
+ /*--------------------------------------------------------------------------------
+ Graph traversal and build functions
+ */
+
+ def lookup( String node_label ,boolean verbose = false ){
+ def lookup_node = node_map[ node_label ]
+ if( !lookup_node ){
+ def match_result
+ for( func in node_f_list ){
+ match_result = func( node_label )
+ if( match_result.status == "matched" ){
+ lookup_node = match_result
+ break
+ }
+ }
+ }
+
+ if( !lookup_node && verbose ) println "lookup:: Node ${node_label} could not be found."
+ return lookup_node
+ }
+
+ def run_build_scripts_f( List root_node_labels ,boolean verbose = true ){
+ if( root_node_labels.isEmpty() ) return
+
+ def node_function = { node ,error_token_set ->
+
+ if( !can_be_built_q( node ) ){
+ println( "Skipping build for ${node.label} due to dependency problems" )
+ return
+ }
+ if( !should_be_built_q( node ) ){
+ if( verbose ) println( "${node.label} already up to date" )
+ return
+ }
+
+ println( "Running build script for ${node.label}" )
+ node.build( node ,node.neighbor )
+
+ if( should_be_built_q( node ) ){
+ println( "Build failed for ${node.label}" )
+ set_mark( node ,'build_failed' )
+ }
+ }
+
+ println( "run_build_scripts_f:: running ..." )
+ all_DAG_DF( root_node_labels ,node_function ,verbose )
+ }
+
+ // Add the rest of your methods here as instance/static methods based on whether they depend on the graph instance
+
+}
/*
Some terms:
@@ -29,7 +207,7 @@
*/
def unpack_file_path = { file_fp ->
- def file_fn = new File(file_fp)
+ def file = new File(file_fp)
// Get the parent directory path
def parent_dp = file_fn.getParent()
@@ -71,7 +249,7 @@ def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as
def leaf_q = { node -> node && node.type && node.type == 'leaf' }
// mark
-def has_mark(node) = { node.mark && !node.mark.isEmpty() }
+def has_mark = { node -> node.mark && !node.mark.isEmpty() }
def set_mark(node ,mark){
node.mark = node.mark ?: [] as Set
node.mark << mark
@@ -446,6 +624,7 @@ def run_build_scripts_f(root_node_labels ,boolean verbose = true){
}
+ println("run_build_scripts_f:: running ...")
// Apply the function to all nodes in a depth-first manner
all_DAG_DF(root_node_labels ,node_function ,verbose)
}
diff --git a/developer/deprecated/.githolder b/developer/deprecated/.githolder
new file mode 100644
index 0000000..e69de29
diff --git a/user/Ariadne_0.1/Ariadne.groovy b/developer/deprecated/Ariandne.groovy
similarity index 98%
rename from user/Ariadne_0.1/Ariadne.groovy
rename to developer/deprecated/Ariandne.groovy
index 6956705..a657f0d 100644
--- a/user/Ariadne_0.1/Ariadne.groovy
+++ b/developer/deprecated/Ariandne.groovy
@@ -29,13 +29,13 @@
*/
def unpack_file_path = { file_fp ->
- def file_fn = new File(file_fp)
+ def file = new File(file_fp) // Renamed to 'file' to avoid overwriting 'file_fn'
// Get the parent directory path
- def parent_dp = file_fn.getParent()
+ def parent_dp = file.getParent()
// Get the file name (with extension)
- def file_fn = file_fn.getName()
+ def file_fn = file.getName()
// Split the file name into base and extension
def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[0..file_fn.lastIndexOf('.') - 1] : file_fn
@@ -71,7 +71,7 @@ def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as
def leaf_q = { node -> node && node.type && node.type == 'leaf' }
// mark
-def has_mark(node) = { node.mark && !node.mark.isEmpty() }
+def has_mark = { node -> node.mark && !node.mark.isEmpty() }
def set_mark(node ,mark){
node.mark = node.mark ?: [] as Set
node.mark << mark
@@ -446,6 +446,7 @@ def run_build_scripts_f(root_node_labels ,boolean verbose = true){
}
+ println("run_build_scripts_f:: running ...")
// Apply the function to all nodes in a depth-first manner
all_DAG_DF(root_node_labels ,node_function ,verbose)
}
diff --git a/developer/deprecated/dependency_graph_definition.groovy b/developer/deprecated/dependency_graph_definition.groovy
new file mode 100644
index 0000000..ec9028b
--- /dev/null
+++ b/developer/deprecated/dependency_graph_definition.groovy
@@ -0,0 +1,387 @@
+/*
+ Here the developer describes the build environment and the graph of
+ dependencies between files to be built.
+
+ Each file is given a 'node'. The node label is a path
+ to the file, existing or not. Node's can also be 'symbolic'
+ in that the label does not correspond to a file.
+
+ File paths are relative to the developer's directory. Also
+ known as $DEVELOPER_HOME.
+
+ The program that processes this file is
+ `executor/build_from_dependency_graph.groovy`. There are more notes and
+ definitions there.
+*/
+
+
+/*--------------------------------------------------------------------------------
+ The build environment
+
+ Each tool has its '_IN' and '_OUT' directories that are referenced in
+ their build scripts. This keeps references in the build script local.
+ (Flow of files between tools can be seen in the overlapping definitions
+ among the various _IN and _OUT directories.)
+
+ These suffixes are sometimes used to eliminate ambiguity:
+
+ _FN = File Name
+ _FP = File Path (last element of the FP is the FN)
+ _DN = Directory Name
+ _DP = Directory path (last element of the DP is the DN)
+
+ _FNL = File Name list
+ _FPL = File Path List
+ _DNL = Directory path list
+ _DPL = Directory path list
+
+ Files in a _LEAF directory can not be built, and should never be deleted by the tools.
+
+*/
+
+def env = [:]
+
+// Required shell environment variables
+def varName_List = [
+ 'REPO_HOME'
+ ,'PROJECT'
+ ,'ENV_BUILD_VERSION'
+ ,'DEVELOPER_HOME'
+]
+varName_List.each { varName ->
+ env[varName] = System.getenv(varName) ?: ""
+}
+
+// Optional shell environment variables
+def varNameOptional_List = [
+ 'CLASSPATH'
+]
+varNameOptional_List.each { varName ->
+ env[varName] = System.getenv(varName) ?: ""
+}
+
+env.CLASSPATH += ":${env.ANTLR_JAR}"
+
+// tools used in build scripts
+def JAVA_COMP_FP = "${env.JAVA_HOME}/bin/javac" // Java compiler path
+def JAVA_INTERP_FP = "${env.JAVA_HOME}/bin/java" // Java interpreter path
+def JAVA_ARCHIVE_FP = "${env.JAVA_HOME}/bin/jar" // Java archive tool path
+
+def dir_map = [
+ 'ANTLR_IN_LEAF' : 'ANTLR/'
+ ,'ANTLR_OUT' : 'javac/ANTLR/'
+ ,'ANTLR_OUT_PARENT' : 'javac/' // required by the antlr command to be a base for OUT
+ ,'EXECUTOR_IN' : 'executor/'
+ ,'JAVA_COMP_IN' : 'javac/'
+ ,'JAVA_COMP_IN_LEAF': 'javac/leaf/'
+ ,'JAVA_COMP_IN_ANTLR' : 'javac/ANTLR/'
+ ,'JAVA_COMP_IN_SYN' : 'javac/synthesized/'
+ ,'JAVA_COMP_OUT' : 'jvm/'
+ ,'JVM_IN' : 'jvm/'
+ ,'TEMP' : 'Erebus/'
+]
+
+env.CLASSPATH += ":${dir_map.JVM_IN}"
+
+dir_map.JAVA_COMP_IN_LIST =
+ "${dir_map.JAVA_COMP_IN_LEAF}"
+ +":${dir_map.JAVA_COMP_IN_ANTLR}"
+ +":${dir_map.JAVA_COMP_IN_SYN}"
+
+//--------------------------------------------------------------------------------
+// required environment variable check. Will add additional preface checks here
+// should they become available.
+//
+
+// a helper function for `environment_check`
+def print_missing_var_list(missing_var_list){
+ if(missing_var_list.isEmpty()){
+ // Print nothing if the list is empty
+ return
+ } else if(missing_var_list.size() == 1){
+ println "This environment variable was not set: ${missing_var_list[0]}"
+ } else {
+ println "These environment variables were not set: ${missing_var_list.join(' ,')}"
+ }
+}
+
+task environment_check {
+ dependsOn ':installTools'
+ doFirst {
+
+ println "CLASSPATH: ${env.CLASSPATH}"
+ println "JAVA_COMP_IN_LIST: ${dir_map.JAVA_COMP_IN_LIST}"
+
+ // did the required variables have definitions?
+ def error_missing = false
+ def error_project = false
+ def missing_var_list = [] // To collect missing environment variables
+ varName_List.each { varName ->
+ if(!env[varName]){
+ error_missing = true
+ missing_var_list << varName
+ }
+ }
+ print_missing_var_list(missing_var_list)
+
+ // did the required variables have definitions?
+ if(env.PROJECT != "GQL_to_Cypher"){
+ error_project = true
+ println "Expected project 'GQL_to_Cypher' ,but found '${env.PROJECT}'."
+ }
+ if(error_missing || error_project){
+ throw new GradleException("Bailing due to missing environment variables.")
+ }
+ }
+ doLast {
+ println "================================================================================"
+ println "Building project .."
+ }
+}
+
+/*--------------------------------------------------------------------------------
+ Map keyed on label node definitions
+
+*/
+
+def node_map = [
+
+ "all" : [
+ ,type: "symbol"
+ ,neighbor: [
+ "ANTLR_OUT_FL"
+ ,"RuleNameList"
+ ,"RuleNameListRegx"
+ ,"Synthesize_SyntaxAnnotate"
+ ,"Synthesize_SyntaxAnnotate_PrintVisitor"
+ ,"Synthesize_SyntaxAnnotate_PrintVisitorMethod"
+ ]
+ ]
+
+ "ANTLR_OUT_FL" : [
+ ,type: "symbol"
+ ,neighbor: ["${dir_map.EXECUTOR_IN}/ANTLR_OUT_FL"]
+ ]
+
+ ,"RuleNameList" : [
+ ,type: "symbol"
+ ,neighbor: ["${dir_map.EXECUTOR_IN}/RuleNameList"]
+ ]
+
+ ,"RuleNameListRegx" : [
+ ,type: "symbol"
+ ,neighbor: ["${dir_map.EXECUTOR_IN}/RuleNameListRegx"]
+ ]
+
+ ,"Synthesize_SyntaxAnnotate" : [
+ ,type: "symbol"
+ ,neighbor: [
+ "${dir_map.JAVA_COMP_IN_LEAF}/StringUtils.java"
+ ,"${dir_map.EXECUTOR_IN}/Synthesize_SyntaxAnnotate"
+ ]
+ ]
+
+ "Synthesize_SyntaxAnnotate.class" : [
+ type: 'path' , // It's a path type node
+ ,neighbor: [
+ "${dir_map.JAVA_COMP_IN_LEAF}/Synthesize_SyntaxAnnotate.java" , // Dependency
+ "${dir_map.JAVA_COMP_IN_LEAF}/StringUtils.java" // Dependency
+ ]
+ ,build: { node ,neighbor ->
+ def javac_cmd = "${JAVA_COMP_FP} -d ${dir_map.JAVA_COMP_OUT} ${neighbor.join(' ')}"
+ javac_cmd.execute().waitFor()
+ }
+ ]
+
+ ,"Synthesize_SyntaxAnnotate_PrintVisitor" : [
+ ,type: "symbol"
+ ,neighbor: [
+ "${dir_map.JAVA_COMP_IN_LEAF}/StringUtils.java"
+ ,"${dir_map.JAVA_COMP_IN_LEAF}/Synthesize_SyntaxAnnotate_PrintVisitorMethod.java"
+ ,"${dir_map.EXECUTOR_IN}/Synthesize_SyntaxAnnotate_PrintVisitor"
+ ]
+ ]
+
+ ,"Synthesize_SyntaxAnnotate_PrintVisitorMethod" : [
+ ,type: "symbol"
+ ,neighbor: [
+ "${dir_map.JAVA_COMP_IN_LEAF}/StringUtils.java"
+ ,"${dir_map.EXECUTOR_IN}/Synthesize_SyntaxAnnotate_PrintVisitorMethod"
+ ]
+ ]
+]
+
+
+
+//--------------------------------------------------------------------------------
+// node making functions
+//
+
+// javac/leaf/ returns leaf node for javac/leaf/
+def node_leaf_f(node_label) {
+ def leafNodePattern = ~/${dir_map['JAVA_COMP_IN_LEAF']}(.*)/
+ def match = node_label =~ leafNodePattern
+ if (!match) {
+ return [status: "no_match"]
+ }
+ def baseName = match[0][1]
+
+ def leafFilePath = "${dir_map['JAVA_COMP_IN_LEAF']}${baseName}"
+ def leafFile = new File(leafFilePath)
+ if (!leafFile.exists()) {
+ return [status: "no_match"]
+ }
+
+ return [
+ status: "matched"
+ ,label: node_label
+ ,type: "leaf"
+ ,neighbor: []
+ ]
+}
+
+// given executor/ returns node to build script wrapping jvm/.jar
+def node_executor_f(node) {
+
+ def match = node =~ /^(executor\/)(${base})$/
+ if (!match) {
+ return [status: "no_match"]
+ }
+ def baseName = match[0][2]
+
+ def jarFilePath = "${dir_map['JVM_IN']}${baseName}.jar"
+ def wrapperFilePath = "${dir_map['EXECUTOR_IN']}${baseName}"
+
+ def jarFile = new File(jarFilePath)
+ if (!jarFile.exists()) {
+ return [status: "no_match"]
+ }
+
+ return [
+ status: "matched"
+ ,label: node
+ ,type: "path"
+ ,neighbor: [jarFilePath]
+ ,must_have: [jarFilePath]
+ ,build: { node ,neighbor ->
+
+ // The script for wrapping the jar file:
+ def wrapper =
+ """
+ #!/usr/bin/env bash
+ ${dir_map['JAVA_INTERP']} -cp \${CLASSPATH}:${dir_map['JVM_IN']}:${dir_map['JVM_IN']}/${baseName}.jar ${baseName} \\\$\\@
+ """
+
+ new File(wrapperFilePath).withWriter('UTF-8') { writer ->
+ writer.write(wrapper)
+ }
+
+ println "Creating executable wrapper script for ${baseName} in executor directory."
+ "chmod +x ${wrapperFilePath}".execute().text
+ }
+ ]
+}
+
+// given javac/ANTLR/.java, returns node to build grammar .g4
+def node_grammar_f(node) {
+
+ def match = node =~ /^(${dir_map['ANTLR_OUT']})(${base})(Lexer|Parser|BaseListener|Listener|BaseVisitor|Visitor)\.java$/
+ if( !match ){
+ return [status: "no_match"]
+ }
+
+ def grammarName = match[0][2]
+ def outputType = match[0][3]
+
+ def grammarFilePath = "${dir_map['ANTLR_IN_LEAF']}${grammarName}.g4"
+ def grammarFile = new File(grammarFilePath)
+
+ if( !grammarFile.exists() ){
+ return [status: "no_match"]
+ }
+
+ return [
+ status: "matched"
+ ,label: node
+ ,type: "path"
+ ,neighbor: [grammarFilePath]
+ ]
+}
+
+// given .class returns node to build .class from .java
+def node_class_f(node) {
+
+ def match = node =~ /^(${dir_map['JAVA_COMP_OUT']})(${base})\.class$/
+ if( !match ){
+ return [status: "no_match"]
+ }
+
+ def baseName = match[0][2]
+ def javaFilePath = "${dir_map['JAVA_COMP_IN_PRIMARY_DIR']}/${baseName}.java"
+ def javaFile = new File(javaFilePath)
+
+ if( !javaFile.exists() ){
+ return [status: "no_match"]
+ }
+
+ return [
+ status: "matched",
+ label: node,
+ type: "path", // It's a path node since we're building the .class file
+ neighbor: [javaFilePath], // The corresponding .java file
+ build: { node, neighbor ->
+ def javac_cmd = "${JAVA_COMP_FP} -d ${dir_map.JAVA_COMP_OUT} -sourcepath ${dir_map.JAVA_COMP_IN_DL} ${neighbor[0]}"
+ javac_cmd.execute().waitFor()
+ }
+ ]
+}
+
+// given .jar returns node to build .jar from .class
+def node_jar_f(node) {
+
+ // Use the symbolic name and base patterns
+ def match = node =~ /^(${dir_map['JAVA_COMP_OUT']})(${base})\.jar$/
+
+ if( !match ){
+ return [status: "no_match"]
+ }
+
+ def baseName = match[0][2]
+ def classFilePath = "${dir_map['JAVA_COMP_OUT']}${baseName}.class"
+ def classFile = new File(classFilePath)
+
+ if( !classFile.exists() ){
+ return [status: "no_match"]
+ }
+
+ return [
+ status: "matched"
+ ,label: node
+ ,type: "path"
+ ,neighbor: [classFilePath]
+ ,build: { node ,neighbor ->
+ println "Building jar for ${baseName}"
+ def command = "${ext.javaHome}/bin/jar cf ${baseName}.jar -C ${dir_map['JAVA_COMP_OUT']} ${baseName}.class"
+ return command.execute().text;
+ }
+ ]
+}
+
+// list of the recognizer functions
+def node_f_list = [
+ node_leaf_f
+ ,node_executor_f
+ ,node_grammar_f
+ ,node_class_f
+ ,node_jar_f
+]
+
+// comprehensions to make regexprs more readable
+def base = "[a-zA-Z0-9_-]+"
+def ext = "[a-zA-Z0-9_-]+$"
+def name = "${base}\\.${ext}"
+def path = ".+/${name}"
+
+
+
+// LocalWords: wellformed
diff --git a/developer/document/cycle_detection.html b/developer/document/cycle_detection.html
new file mode 100644
index 0000000..3c9fb85
--- /dev/null
+++ b/developer/document/cycle_detection.html
@@ -0,0 +1,105 @@
+
+
+
+ Dependency Build Algorithm
+
+
+
+ Cycle Detection in Dependency Graph
+ Overview
+
+ The is_acyclic_q function is designed to detect cycles in a dependency graph using a depth-first search (DFS) algorithm. It starts from a list of root node labels and traverses the graph to ensure that there are no cycles. If a cycle is detected, the function marks the nodes involved and continues to explore other parts of the graph.
+
+ Key Concepts
+
+ - Dependency Graph: A graph where nodes represent build targets and edges represent dependencies between these targets.
+ - Depth-First Search (DFS): An algorithm for traversing or searching tree or graph data structures. It starts at the root and explores as far as possible along each branch before backtracking.
+ - Cycle Detection: The process of identifying cycles (loops) in a graph, where a cycle is a path that starts and ends at the same node.
+
+ Functions
+ 1. is_acyclic_q
+
+ Purpose: To determine if the dependency graph is acyclic (i.e., contains no cycles).
+
+
+ Parameters:
+
+ root_node_labels: A list of labels for the root nodes to start the cycle search.
+ verbose: A boolean flag for enabling detailed output (default is true).
+
+
+
+ Returns:
+
+ 'acyclic' if no cycles are found.
+ 'cycle_found' if cycles are detected.
+
+
+
+ Process:
+
+ - Initializes a stack for DFS traversal.
+ - Iteratively calls the
is_acyclic_q_descend function to traverse the graph and detect cycles.
+ - Updates the traversal state and continues exploring other paths until the stack is empty.
+
+
+ 2. is_acyclic_q_descend
+
+ Purpose: To perform the actual DFS traversal and cycle detection for a given path.
+
+
+ Parameters:
+
+ path_stack: A stack representing the current path in the graph.
+ verbose: A boolean flag for enabling detailed output (default is true).
+
+
+
+ Returns:
+
+ 'leaf_node' if the current node has no children.
+ 'cycle_found' if a cycle is detected.
+
+
+
+ Process:
+
+ - Collects the current path and node.
+ - Checks for cycles by comparing the current node with nodes in the path.
+ - Marks nodes involved in cycles and updates the stack to continue traversal.
+
+
+ Usage
+
+ The is_acyclic_q function is used to ensure that the dependency graph defined in the build file is free of cycles. This is crucial for preventing infinite loops and ensuring that the build process can proceed smoothly.
+
+
+
diff --git a/developer/document/dependency_graph.html b/developer/document/dependency_graph.html
new file mode 100644
index 0000000..378c52d
--- /dev/null
+++ b/developer/document/dependency_graph.html
@@ -0,0 +1,137 @@
+
+
+
+ Dependency Build Algorithm
+
+
+
+ Cycle Detection in Dependency Graph
+ Overview
+
+ The is_acyclic_q function is designed to detect cycles in a dependency graph using a depth-first search (DFS) algorithm. It starts from a list of root node labels and traverses the graph to ensure that there are no cycles. If a cycle is detected, the function marks the nodes involved and continues to explore other parts of the graph.
+
+ Key Concepts
+
+ - Dependency Graph: A graph where nodes represent build targets and edges represent dependencies between these targets.
+ - Depth-First Search (DFS): An algorithm for traversing or searching tree or graph data structures. It starts at the root and explores as far as possible along each branch before backtracking.
+ - Cycle Detection: The process of identifying cycles (loops) in a graph, where a cycle is a path that starts and ends at the same node.
+
+ Functions
+ 1. is_acyclic_q
+
+ Purpose: To determine if the dependency graph is acyclic (i.e., contains no cycles).
+
+
+ Parameters:
+
+ root_node_labels: A list of labels for the root nodes to start the cycle search.
+ verbose: A boolean flag for enabling detailed output (default is true).
+
+
+
+ Returns:
+
+ 'acyclic' if no cycles are found.
+ 'cycle_found' if cycles are detected.
+
+
+
+ Process:
+
+ - Initializes a stack for DFS traversal.
+ - Iteratively calls the
is_acyclic_q_descend function to traverse the graph and detect cycles.
+ - Updates the traversal state and continues exploring other paths until the stack is empty.
+
+
+ 2. is_acyclic_q_descend
+
+ Purpose: To perform the actual DFS traversal and cycle detection for a given path.
+
+
+ Parameters:
+
+ path_stack: A stack representing the current path in the graph.
+ verbose: A boolean flag for enabling detailed output (default is true).
+
+
+
+ Returns:
+
+ 'leaf_node' if the current node has no children.
+ 'cycle_found' if a cycle is detected.
+
+
+
+ Process:
+
+ - Collects the current path and node.
+ - Checks for cycles by comparing the current node with nodes in the path.
+ - Marks nodes involved in cycles and updates the stack to continue traversal.
+
+
+ Usage
+
+ The is_acyclic_q function is used to ensure that the dependency graph defined in the build file is free of cycles. This is crucial for preventing infinite loops and ensuring that the build process can proceed smoothly.
+
+
+
+
+
+2. Run Build Scripts
+
+ - Traverse the queue starting from the tail (the most recently added nodes).
+ - For each node, attempt to build it by checking the file dates of the target node and its dependencies:
+
+ - If the target file is older than any dependency, execute the nodeâs build function.
+ - After building, recheck the file dates. If the file date has been updated, mark the node as successfully built.
+ - If the build fails or the file date is not updated, mark the node with an error in its property list.
+
+
+ - Nodes with dependencies marked with errors will not be built, and errors will propagate up the dependency tree.
+
+
+3. Final Reporting and Status
+
+ - If the root node is successfully built, report the success and any other successfully built nodes.
+ - If an error has propagated to the root, report the failure.
+ - Keep a list of all successfully built nodes and provide a final summary of the build status.
+
+
+4. Node Definitions
+Each node in the dependency graph is defined by a property dictionary. A node is either a symbol or a path:
+
+ - Symbol Nodes: These represent abstract concepts or commands and always trigger a build unless marked with an error.
+ - Path Nodes: These represent file paths. A path node is considered built if its target file is newer than its dependencies.
+
+Both node types are identified by a label, and their dependencies are stored as a list of node labels. The presence of an error property indicates that the node has failed to build or encountered a problem during processing.
+
+