+++ /dev/null
-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:
-
- a node is either malformed or 'wellformed'. A wellformed node meets
- the criteria set forth by `well_formed_node_q`.
-
- 'node': a dictionary, hopefully a wellformed one
- 'file node': a 'path' or 'leaf' type node.
- 'node file': for 'path' or 'leaf' nodes, path relative to the developer's directory.
-
- This code makes use of the following node properties:
- - label: Unique label string. Used to reference nodes.
- - type: The type of the node: 'leaf' ,'symbol' ,or 'path'.
- - neighbor: list of the neighbor nodes labels
- - must_have: list of node labels
- - build: Code that builds the node.
- - mark: a set of 'mark' tokens optionally placed on a node
-
- For 'path' and 'leaf' type nodes, the node label is a file path. Relative paths
- are relative to the developer's directory.
-
- The dependency graph is defined by the `lookup` function. `lookup` when given
- a node label returns the node, if found, or null.
-
-*/
-
-/*--------------------------------------------------------------------------------
- file utility functions
-*/
-
-def unpack_file_path = { file_fp ->
- def file = new File(file_fp)
-
- // Get the parent directory path
- def parent_dp = file_fn.getParent()
-
- // Get the file name (with extension)
- def file_fn = file_fn.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
- def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[file_fn.lastIndexOf('.') + 1..-1] : ''
-
- // Return the components as a map for easier use
- return [
- parent_dp: parent_dp
- ,file_fn: file_fn
- ,file_fn_base: file_fn_base
- ,file_fn_ext: file_fn_ext
- ]
-}
-
-def file_exists_q(node_label) {
- def node_path = Paths.get(node_label)
- return Files.exists(node_path)
-}
-
-
-/*-------------------------------------------------------------------------------
- DAG adjectives
-
- node undefined for undefined node?
-*/
-
-// A node is type 'leaf' when the node file can not be deleted or replaced.
-// This type used to be called 'primary', which might be a better term.
-
-def all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
-
-def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
-def leaf_q = { node -> node && node.type && node.type == 'leaf' }
-
-// mark
-def has_mark = { node -> node.mark && !node.mark.isEmpty() }
-def set_mark(node ,mark){
- node.mark = node.mark ?: [] as Set
- node.mark << mark
-}
-def clear_mark(node ,mark){
- if( node.mark ) node.mark.remove(mark)
-}
-def marked_good_q(node){
- return (
- node
- && node.mark
- && ('wellformed' in node.mark)
- && !('cycle_member' in node.mark)
- && !('build_failed' in node.mark)
- )
-}
-
-
-
-/*--------------------------------------------------------------------------------
- Wellformed Node Check
-
- The developer defines the nodes, so their form needs to be checked.
-
- early bail on node not defined
- legal path in the build directory makedir -p ... not really a acquirement
-
-*/
-def 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'
-]
-def wellformed_q = { 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'
- }
-
- // Removed test about symbol build needing neighbors
-
- if(
- node.type == "path"
- && (!node.build || !(node.build instanceof Closure))
- )
- form_error_set << 'missing_required_build_code'
-
- // Removed test about path needing neighbors
-
- // Can a primary file have neighbors or a build script? Our build model
- // has the node file as a target, the build script as a means of building
- // the target (hence its name), and the dependencies as inputs to the build
- // script. So in the spirit of this model, we will say "no".
- 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
-}
-
-
-
-/*--------------------------------------------------------------------------------
- A well formed graph checker. Traverses entire graph and marks nodes
- that are not well formed or that are part of a cycle.
-
-*/
-
-// given a node label list, adds the 'wellformed' mark to wellformed nodes.
-def mark_the_wellformed_f(node_label_list ,boolean verbose = true){
- def all_wellformed = true
-
- def neighbors = node_label_list.collect{ neighbor_label ->
- def neighbor_node = lookup(neighbor_label)
- def form_errors = wellformed_q(neighbor_node)
- if(form_errors.isEmpty()){
- neighbor_node.mark = neighbor_node.mark ?: [] as Set
- neighbor_node.mark << 'wellformed'
- } else {
- all_wellformed = false
- if(verbose){
- if(neighbor_node.label && neighbor_node.label.length() > 0){
- print("node ${neighbor_node.label} is malformed due to:")
- } else {
- print("anonymous node is malformed due to:")
- }
- form_errors.each { error -> print(" ${error}") }
- println("")
- }
- }
- neighbor_label
- }
-
- return all_wellformed ? 'all_wellformed' : 'exists_malformed'
-}
-
-// descends un-visited leftmost path, while marking nodes as wellformed and if
-// they are part of a cycle.
-def markup_graph_f_descend(path_stack ,boolean verbose = true){
- def ret_value = [] as Set
- def local_path = path_stack.collect{ it[0] }
- def local_node_label = local_path[-1]
- def cycle_start_index
-
- do{
- // Check for a cycle in the local path
- cycle_start_index = local_path[0..-2].findIndexOf{ it == local_node_label }
- if(cycle_start_index != -1){ // Cycle detected
- ret_value << 'cycle_found'
- if(verbose) print "markup_graph_f_descend:: dependency cycle found:"
- local_path[cycle_start_index..-1].each{ cycle_node_label ->
- def cycle_node = lookup(cycle_node_label)
- if(verbose) print " ${cycle_node.label}"
- cycle_node.mark = cycle_node.mark ?: [] as Set // Initialize mark set if needed
- cycle_node.mark << 'cycle_member'
- }
- if(verbose) println ""
- // we can not continue searching after the loop so ,we pop back to treat
- // the first node in the loop as though a leaf node.
- path_stack = path_stack[0..cycle_start_index]
- return ret_value
- }
-
- // a 'de-facto' leaf node test subtleties here because we have not yet
- // determined if the nodes we are wellformed. This is purposeful ,as
- // this function does not know about the relationships between the
- // possible error marks.
- def local_node = lookup(local_node_label)
- if(local_node.neighbor.isEmpty()){
- ret_value << 'defacto_leaf_node'
- return ret_value
- }
-
- // Mark the wellformed nodes and get the result
- def result = mark_the_wellformed_f(local_node.neighbor ,verbose)
- if(result == 'exists_malformed'){
- ret_value << 'exists_malformed'
- }
-
- // Descend further into the tree.
- path_stack << local_node.neighbor.clone()
- local_node_label = local_node.neighbor[0]
- local_path << local_node_label
- }while(true)
-}
-
-
-
-/*--------------------------------------------------------------------------------
- This function defines the graph.
-
- Lookup attempts to lookup a node label in the node_map, and failing that, it
- tries each label pattern recognition function in order.
-
- lookup_marked_good can be run after `wellformed_graph_f` has marked up the
- graph. It will only return a node if a) found b) that is 'good'.
- Note the `marked_good_q` adjective above.
-
-*/
-def lookup(node_label ,verbose = false){
- def lookup_node = node_map[node_label]
- if( lookup_node ){
- lookup_node.label = node_label
- } else {
- 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 ){
- if( verbose ) println "lookup:: Node ${node_label} could not be found."
- return null
- }
- return lookup_node
-}
-
-// mark aware lookup function
-def lookup_marked_good(node_label ,verbose = false){
- def node = lookup(node_label ,verbose)
- if( node && marked_good_q(node) ) return node;
- return null;
-}
-
-
-/*
- Given `root_node_labels` of a DAG. Applies `node_function` to each node in a
- depth-first traversal order. Returns a set of error tokens encountered
- during traversal.
-
- `wellformed_graph_q` must be run on the DAG before this function is called ,or
- `lookup_marked_good` will not function correctly.
-*/
-def all_DAG_DF(root_node_labels ,node_function ,boolean verbose = true) {
- def error_token_set = [] as Set
-
- if (root_node_labels.isEmpty()) return error_token_set
-
- def visited = [] as Set
- def in_traversal_order = []
- def stack = []
-
- root_node_labels.each { root_label ->
- stack << root_label
- }
-
- do {
- def node_label = stack.pop()
-
- def node = lookup_marked_good(node_label ,verbose)
- if (!node) {
- error_token_set << 'lookup_fail'
- continue
- }
-
- if (node.label in visited) continue
- visited << node.label
-
- in_traversal_order << node
-
- node.neighbor.each { neighbor_label ->
- stack << neighbor_label
- }
- } while (!stack.isEmpty())
-
- in_traversal_order.reverse().each { node ->
- node_function(node ,error_token_set ,verbose)
- }
-
- return error_token_set
-}
-
-
-/*--------------------------------------------------------------------------------
- run the build scripts
- depends upon is_acyclic having already marked up the graph.
-*/
-
-import java.nio.file.Files
-import java.nio.file.Paths
-
-// a symbol dependency is good ,as long as it is built before the node in question
-def good_dependency_q(node_labels) {
- return node_labels.every { node_label ->
- def node = lookup_marked_good(node_label)
- if (!node) return false
- if (node.type in ['path' ,'leaf'] && !file_exists_q(node.label)) return false
- return true
- }
-}
-
-/*
- Given a node label and a list of node labels ,returns true if the file at the
- node label in the first argument is newer than all the files at the
- corresponding node labels in the second list.
-*/
-def newer_than_all(node_label ,node_label_list) {
- def node_path = Paths.get(node_label)
- if (!Files.exists(node_path)) return false
-
- def node_last_modified = Files.getLastModifiedTime(node_path).toMillis()
-
- return node_label_list.every { label ->
- def path = Paths.get(label)
- if (!Files.exists(path)) return false
- def last_modified = Files.getLastModifiedTime(path).toMillis()
- return node_last_modified > last_modified
- }
-}
-
-def can_be_built_q(node){
- if( !marked_good_q(node) ) return false;
- if(
- (node.type == 'symbol' || type == 'path')
- && !good_dependency_q( node.neighbor )
- ){
- return false
- }
- if(
- node.type == 'leaf'
- && !file_exists_q(node.label)
- ){
- return false;
- }
- return true
-}
-
-// `can_be_build_q` must be true for this to be meaningful:
-def should_be_built_q(node ,verbose = true) {
- if(node.type == 'leaf') return false
- if(node.type == 'symbol') return true
- if( node.type == 'path') return !newer_than_all(node.label ,node.neighbor)
- println("should_be_build_q:: unrecognized node type ,so assuming it should not be built.")
- return false
-}
-
-/*
- Runs build scripts for the given root_node_labels in a depth-first traversal order.
- Uses `all_DAG_DF` to traverse the graph and applies the build function to each node.
-
- Be sure that `wellformed_graph_q` has been run on DAG.
-
- Be sure that the 'build_failed' marks have been cleared if this is not the
- first build attempt.
-
-*/
-def run_build_scripts_f(root_node_labels ,boolean verbose = true){
- if( root_node_labels.isEmpty() ) return
-
- // Define the function to be applied to each node
- def node_function = { node ,error_token_set ->
-
- if( !can_be_built_q(node) ){
- println("Skipping build for ${node.label} due to dependency problems or found leaf is missing")
- return
- }
-
- if( !should_be_built_q(node) ){
- if(verbose) println("${node.label} already up to date")
- return
- }
-
- // if we get here, node can and should be built
-
- println("Running build script for ${node.label}")
- node.build(node ,node.neighbor)
-
- // Check if the build updated the target file
- if( should_be_built_q(node) ){
- println("Build failed for ${node.label}")
- set_mark(node ,'build_failed')
- }
-
- }
-
- 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)
-}
-
-
-// LocalWords: FN FPL DN DNL RuleNameListRegx RuleNameList PrintVisitorMethod
-// LocalWords: PrintVisitor SyntaxAnnotate wellformed defacto acyclic
-// LocalWords: wellformed unvisited
--- /dev/null
+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:
+
+ a node is either malformed or 'wellformed'. A wellformed node meets
+ the criteria set forth by `well_formed_node_q`.
+
+ 'node': a dictionary, hopefully a wellformed one
+ 'file node': a 'path' or 'leaf' type node.
+ 'node file': for 'path' or 'leaf' nodes, path relative to the developer's directory.
+
+ This code makes use of the following node properties:
+ - label: Unique label string. Used to reference nodes.
+ - type: The type of the node: 'leaf' ,'symbol' ,or 'path'.
+ - neighbor: list of the neighbor nodes labels
+ - must_have: list of node labels
+ - build: Code that builds the node.
+ - mark: a set of 'mark' tokens optionally placed on a node
+
+ For 'path' and 'leaf' type nodes, the node label is a file path. Relative paths
+ are relative to the developer's directory.
+
+ The dependency graph is defined by the `lookup` function. `lookup` when given
+ a node label returns the node, if found, or null.
+
+*/
+
+/*--------------------------------------------------------------------------------
+ file utility functions
+*/
+
+def unpack_file_path = { file_fp ->
+ def file = new File(file_fp)
+
+ // Get the parent directory path
+ def parent_dp = file_fn.getParent()
+
+ // Get the file name (with extension)
+ def file_fn = file_fn.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
+ def file_fn_ext = file_fn.lastIndexOf('.') > 0 ? file_fn[file_fn.lastIndexOf('.') + 1..-1] : ''
+
+ // Return the components as a map for easier use
+ return [
+ parent_dp: parent_dp
+ ,file_fn: file_fn
+ ,file_fn_base: file_fn_base
+ ,file_fn_ext: file_fn_ext
+ ]
+}
+
+def file_exists_q(node_label) {
+ def node_path = Paths.get(node_label)
+ return Files.exists(node_path)
+}
+
+
+/*-------------------------------------------------------------------------------
+ DAG adjectives
+
+ node undefined for undefined node?
+*/
+
+// A node is type 'leaf' when the node file can not be deleted or replaced.
+// This type used to be called 'primary', which might be a better term.
+
+def all_node_type_set = ['symbol' ,'path' ,'leaf' ,'generator'] as Set
+
+def persistent_node_mark_set = ['cycle_member' ,'wellformed' ,'build_failed'] as Set
+def leaf_q = { node -> node && node.type && node.type == 'leaf' }
+
+// mark
+def has_mark = { node -> node.mark && !node.mark.isEmpty() }
+def set_mark(node ,mark){
+ node.mark = node.mark ?: [] as Set
+ node.mark << mark
+}
+def clear_mark(node ,mark){
+ if( node.mark ) node.mark.remove(mark)
+}
+def marked_good_q(node){
+ return (
+ node
+ && node.mark
+ && ('wellformed' in node.mark)
+ && !('cycle_member' in node.mark)
+ && !('build_failed' in node.mark)
+ )
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ Wellformed Node Check
+
+ The developer defines the nodes, so their form needs to be checked.
+
+ early bail on node not defined
+ legal path in the build directory makedir -p ... not really a acquirement
+
+*/
+def 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'
+]
+def wellformed_q = { 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'
+ }
+
+ // Removed test about symbol build needing neighbors
+
+ if(
+ node.type == "path"
+ && (!node.build || !(node.build instanceof Closure))
+ )
+ form_error_set << 'missing_required_build_code'
+
+ // Removed test about path needing neighbors
+
+ // Can a primary file have neighbors or a build script? Our build model
+ // has the node file as a target, the build script as a means of building
+ // the target (hence its name), and the dependencies as inputs to the build
+ // script. So in the spirit of this model, we will say "no".
+ 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
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ A well formed graph checker. Traverses entire graph and marks nodes
+ that are not well formed or that are part of a cycle.
+
+*/
+
+// given a node label list, adds the 'wellformed' mark to wellformed nodes.
+def mark_the_wellformed_f(node_label_list ,boolean verbose = true){
+ def all_wellformed = true
+
+ def neighbors = node_label_list.collect{ neighbor_label ->
+ def neighbor_node = lookup(neighbor_label)
+ def form_errors = wellformed_q(neighbor_node)
+ if(form_errors.isEmpty()){
+ neighbor_node.mark = neighbor_node.mark ?: [] as Set
+ neighbor_node.mark << 'wellformed'
+ } else {
+ all_wellformed = false
+ if(verbose){
+ if(neighbor_node.label && neighbor_node.label.length() > 0){
+ print("node ${neighbor_node.label} is malformed due to:")
+ } else {
+ print("anonymous node is malformed due to:")
+ }
+ form_errors.each { error -> print(" ${error}") }
+ println("")
+ }
+ }
+ neighbor_label
+ }
+
+ return all_wellformed ? 'all_wellformed' : 'exists_malformed'
+}
+
+// descends un-visited leftmost path, while marking nodes as wellformed and if
+// they are part of a cycle.
+def markup_graph_f_descend(path_stack ,boolean verbose = true){
+ def ret_value = [] as Set
+ def local_path = path_stack.collect{ it[0] }
+ def local_node_label = local_path[-1]
+ def cycle_start_index
+
+ do{
+ // Check for a cycle in the local path
+ cycle_start_index = local_path[0..-2].findIndexOf{ it == local_node_label }
+ if(cycle_start_index != -1){ // Cycle detected
+ ret_value << 'cycle_found'
+ if(verbose) print "markup_graph_f_descend:: dependency cycle found:"
+ local_path[cycle_start_index..-1].each{ cycle_node_label ->
+ def cycle_node = lookup(cycle_node_label)
+ if(verbose) print " ${cycle_node.label}"
+ cycle_node.mark = cycle_node.mark ?: [] as Set // Initialize mark set if needed
+ cycle_node.mark << 'cycle_member'
+ }
+ if(verbose) println ""
+ // we can not continue searching after the loop so ,we pop back to treat
+ // the first node in the loop as though a leaf node.
+ path_stack = path_stack[0..cycle_start_index]
+ return ret_value
+ }
+
+ // a 'de-facto' leaf node test subtleties here because we have not yet
+ // determined if the nodes we are wellformed. This is purposeful ,as
+ // this function does not know about the relationships between the
+ // possible error marks.
+ def local_node = lookup(local_node_label)
+ if(local_node.neighbor.isEmpty()){
+ ret_value << 'defacto_leaf_node'
+ return ret_value
+ }
+
+ // Mark the wellformed nodes and get the result
+ def result = mark_the_wellformed_f(local_node.neighbor ,verbose)
+ if(result == 'exists_malformed'){
+ ret_value << 'exists_malformed'
+ }
+
+ // Descend further into the tree.
+ path_stack << local_node.neighbor.clone()
+ local_node_label = local_node.neighbor[0]
+ local_path << local_node_label
+ }while(true)
+}
+
+
+
+/*--------------------------------------------------------------------------------
+ This function defines the graph.
+
+ Lookup attempts to lookup a node label in the node_map, and failing that, it
+ tries each label pattern recognition function in order.
+
+ lookup_marked_good can be run after `wellformed_graph_f` has marked up the
+ graph. It will only return a node if a) found b) that is 'good'.
+ Note the `marked_good_q` adjective above.
+
+*/
+def lookup(node_label ,verbose = false){
+ def lookup_node = node_map[node_label]
+ if( lookup_node ){
+ lookup_node.label = node_label
+ } else {
+ 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 ){
+ if( verbose ) println "lookup:: Node ${node_label} could not be found."
+ return null
+ }
+ return lookup_node
+}
+
+// mark aware lookup function
+def lookup_marked_good(node_label ,verbose = false){
+ def node = lookup(node_label ,verbose)
+ if( node && marked_good_q(node) ) return node;
+ return null;
+}
+
+
+/*
+ Given `root_node_labels` of a DAG. Applies `node_function` to each node in a
+ depth-first traversal order. Returns a set of error tokens encountered
+ during traversal.
+
+ `wellformed_graph_q` must be run on the DAG before this function is called ,or
+ `lookup_marked_good` will not function correctly.
+*/
+def all_DAG_DF(root_node_labels ,node_function ,boolean verbose = true) {
+ def error_token_set = [] as Set
+
+ if (root_node_labels.isEmpty()) return error_token_set
+
+ def visited = [] as Set
+ def in_traversal_order = []
+ def stack = []
+
+ root_node_labels.each { root_label ->
+ stack << root_label
+ }
+
+ do {
+ def node_label = stack.pop()
+
+ def node = lookup_marked_good(node_label ,verbose)
+ if (!node) {
+ error_token_set << 'lookup_fail'
+ continue
+ }
+
+ if (node.label in visited) continue
+ visited << node.label
+
+ in_traversal_order << node
+
+ node.neighbor.each { neighbor_label ->
+ stack << neighbor_label
+ }
+ } while (!stack.isEmpty())
+
+ in_traversal_order.reverse().each { node ->
+ node_function(node ,error_token_set ,verbose)
+ }
+
+ return error_token_set
+}
+
+
+/*--------------------------------------------------------------------------------
+ run the build scripts
+ depends upon is_acyclic having already marked up the graph.
+*/
+
+import java.nio.file.Files
+import java.nio.file.Paths
+
+// a symbol dependency is good ,as long as it is built before the node in question
+def good_dependency_q(node_labels) {
+ return node_labels.every { node_label ->
+ def node = lookup_marked_good(node_label)
+ if (!node) return false
+ if (node.type in ['path' ,'leaf'] && !file_exists_q(node.label)) return false
+ return true
+ }
+}
+
+/*
+ Given a node label and a list of node labels ,returns true if the file at the
+ node label in the first argument is newer than all the files at the
+ corresponding node labels in the second list.
+*/
+def newer_than_all(node_label ,node_label_list) {
+ def node_path = Paths.get(node_label)
+ if (!Files.exists(node_path)) return false
+
+ def node_last_modified = Files.getLastModifiedTime(node_path).toMillis()
+
+ return node_label_list.every { label ->
+ def path = Paths.get(label)
+ if (!Files.exists(path)) return false
+ def last_modified = Files.getLastModifiedTime(path).toMillis()
+ return node_last_modified > last_modified
+ }
+}
+
+def can_be_built_q(node){
+ if( !marked_good_q(node) ) return false;
+ if(
+ (node.type == 'symbol' || type == 'path')
+ && !good_dependency_q( node.neighbor )
+ ){
+ return false
+ }
+ if(
+ node.type == 'leaf'
+ && !file_exists_q(node.label)
+ ){
+ return false;
+ }
+ return true
+}
+
+// `can_be_build_q` must be true for this to be meaningful:
+def should_be_built_q(node ,verbose = true) {
+ if(node.type == 'leaf') return false
+ if(node.type == 'symbol') return true
+ if( node.type == 'path') return !newer_than_all(node.label ,node.neighbor)
+ println("should_be_build_q:: unrecognized node type ,so assuming it should not be built.")
+ return false
+}
+
+/*
+ Runs build scripts for the given root_node_labels in a depth-first traversal order.
+ Uses `all_DAG_DF` to traverse the graph and applies the build function to each node.
+
+ Be sure that `wellformed_graph_q` has been run on DAG.
+
+ Be sure that the 'build_failed' marks have been cleared if this is not the
+ first build attempt.
+
+*/
+def run_build_scripts_f(root_node_labels ,boolean verbose = true){
+ if( root_node_labels.isEmpty() ) return
+
+ // Define the function to be applied to each node
+ def node_function = { node ,error_token_set ->
+
+ if( !can_be_built_q(node) ){
+ println("Skipping build for ${node.label} due to dependency problems or found leaf is missing")
+ return
+ }
+
+ if( !should_be_built_q(node) ){
+ if(verbose) println("${node.label} already up to date")
+ return
+ }
+
+ // if we get here, node can and should be built
+
+ println("Running build script for ${node.label}")
+ node.build(node ,node.neighbor)
+
+ // Check if the build updated the target file
+ if( should_be_built_q(node) ){
+ println("Build failed for ${node.label}")
+ set_mark(node ,'build_failed')
+ }
+
+ }
+
+ 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)
+}
+
+
+// LocalWords: FN FPL DN DNL RuleNameListRegx RuleNameList PrintVisitorMethod
+// LocalWords: PrintVisitor SyntaxAnnotate wellformed defacto acyclic
+// LocalWords: wellformed unvisited
--- /dev/null
+package Ariadne;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+
+public class Graph {
+
+ /*--------------------------------------------------------------------------------
+ type aliases
+ */
+ public class TokenSet extends HashSet<Token>{}
+ public class LabelList extends ArrayList<Label> {}
+ public interface Node extends Map<Label ,Object>{}
+ public interface NodeList extends List<Node>{}
+ public interface RecognizerF extends Function<Label ,Node>{}
+ public class RecognizerFList extends ArrayList<RecognizerF> {}
+
+ /*--------------------------------------------------------------------------------
+ instance data
+ */
+
+ private static boolean debug = true;
+ private Map<Label ,Node> node_map;
+ private RecognizerFList recognizer_f_list;
+
+ /*--------------------------------------------------------------------------------
+ constructors
+ */
+
+ public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
+ if (node_map == null && recognizer_f_list == null) {
+ System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
+ System.exit(1);
+ }
+
+ // Initialize node_map and recognizer_f_list to empty collections if they are null
+ this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
+
+ this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
+ }
+
+ /*--------------------------------------------------------------------------------
+ file utilities
+ */
+
+ public static Map<String ,String> unpack_file_path(String file_fp){
+ if(debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
+
+ File file = new File(file_fp);
+ String parent_dp = (file.getParent() != null) ? file.getParent() : "";
+
+ if( !parent_dp.isEmpty() && !parent_dp.endsWith(File.separator) ){
+ parent_dp += File.separator;
+ }
+
+ String file_fn = file.getName();
+ String file_fn_base = file_fn;
+ String file_fn_ext = "";
+
+ int last_index = file_fn.lastIndexOf('.');
+ if(last_index > 0){
+ file_fn_base = file_fn.substring(0 ,last_index);
+ if( last_index + 1 < file_fn.length() ){
+ file_fn_ext = file_fn.substring(last_index + 1);
+ }
+ }
+
+ Map<String ,String> ret_val = new HashMap<>();
+ ret_val.put("dp" ,parent_dp);
+ ret_val.put("fn" ,file_fn);
+ ret_val.put("fn_base" ,file_fn_base);
+ ret_val.put("fn_ext" ,file_fn_ext);
+
+ if(debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
+
+ return ret_val;
+ }
+
+ public static boolean file_exists_q(Label node_label){
+ Path node_path = Paths.get(node_label);
+ return Files.exists(node_path);
+ }
+
+ /*--------------------------------------------------------------------------------
+ About nodes
+
+ A leaf type node specifies a path to a file that should not be deleted by
+ in clean operations. Typically this is the source code. We could add a
+ tool to lock permissions on these before a build, so that the build
+ scripts will also not mess with them (unless they change permissions).
+
+ If the user has multiple dependency graphs defined, a node with no
+ dependencies in one graph, might have dependencies in another.
+
+ An error type node, is one that was found to not have a type, or
+ was constructed by the tool to be a place older, perhaps for a
+ node label that was not found.
+
+ */
+ public static TokenSet all_node_type_set = new TokenSet();
+ static {
+ all_node_type_set.add(new Token("symbol"));
+ all_node_type_set.add(new Token("path"));
+ all_node_type_set.add(new Token("leaf"));
+ all_node_type_set.add(new Token("error"));
+ }
+
+ public static TokenSet persistent_node_mark_set = new TokenSet();
+ static {
+ persistent_node_mark_set.add(new Token("cycle_member"));
+ persistent_node_mark_set.add(new Token("wellformed"));
+ persistent_node_mark_set.add(new Token("build_failed"));
+ persistent_node_mark_set.add(new Token("null_node"));
+ }
+
+ public static boolean leaf_q(Node node){
+ return node != null && "leaf".equals(node.get("type"));
+ }
+
+ public static boolean has_mark(Node node){
+ return node != null && node.get("mark") != null && !( (TokenSet)node.get("mark") ).isEmpty();
+ }
+
+ public static void set_mark(Node node ,Token mark){
+ if( node.get("mark") == null ){
+ node.put("mark" ,new HashTokenSet());
+ }
+ ( (TokenSet)node.get("mark") ).add(mark);
+ }
+
+ public static void clear_mark(Node node ,Token mark){
+ if( node != null && node.get("mark") != null ){
+ ( (TokenSet) node.get("mark") ).remove(mark);
+ }
+ }
+
+ public static boolean marked_good_q(Node node){
+ return node != null && node.get("mark") != null
+ && ( (TokenSet)node.get("mark") ).contains("wellformed")
+ && !( (TokenSet)node.get("mark") ).contains("cycle_member")
+ && !( (TokenSet)node.get("mark") ).contains("build_failed");
+ }
+
+
+ /*--------------------------------------------------------------------------------
+ Well-formed Node Check
+ */
+
+ public static TokenSet form_condition_set = new TokenSet();
+ static {
+ form_condition_set.add(new Token("no_node"));
+ form_condition_set.add(new Token("node_must_have_label"));
+ form_condition_set.add(new Token("label_must_be_string"));
+ form_condition_set.add(new Token("node_must_have_type"));
+ form_condition_set.add(new Token("bad_node_type"));
+ form_condition_set.add(new Token("neighbor_value_must_be_list"));
+ form_condition_set.add(new Token("neighbor_reference_must_be_string"));
+ form_condition_set.add(new Token("neighbor_label_not_in_graph"));
+ form_condition_set.add(new Token("mark_property_value_must_be_set"));
+ form_condition_set.add(new Token("unregistered_mark"));
+ form_condition_set.add(new Token("missing_required_build_code"));
+ form_condition_set.add(new Token("leaf_given_neighbor_property"));
+ form_condition_set.add(new Token("leaf_given_build_property"));
+ }
+
+ // given a node, collects a description of its form, returns a set form condition tokens
+ public static TokenSet wellformed_node_q(Node node){
+ TokenSet form_error_set = new HashSet<>();
+
+ if(node == null){
+ form_error_set.add("null_node");
+ return form_error_set;
+ }
+
+ if( !node.containsKey("label") )
+ form_error_set.add("node_must_have_label");
+ else if( !(node.get("label") instanceof Label) )
+ form_error_set.add("label_must_be_string");
+
+ if( !node.containsKey("type") )
+ form_error_set.add("node_must_have_type");
+ else if( !(node.get("type") instanceof String) || !all_node_type_set.contains(node.get("type")) )
+ form_error_set.add("bad_node_type");
+
+ if( node.containsKey("neighbor") ){
+ if( !(node.get("neighbor") instanceof List) )
+ form_error_set.add("neighbor_value_must_be_list");
+ else if( !((List<?>) node.get("neighbor")).stream().allMatch(it -> it instanceof Label) )
+ form_error_set.add("neighbor_reference_must_be_string");
+ }
+
+ if( node.containsKey("mark") ){
+ if( !(node.get("mark") instanceof Set) )
+ form_error_set.add("mark_property_value_must_be_set");
+ else if( !((Set<?>) node.get("mark")).stream().allMatch(it -> persistent_node_mark_set.contains(it)) )
+ form_error_set.add("unregistered_mark");
+ }
+
+ if( "path".equals(node.get("type")) && (!node.containsKey("build") || !(node.get("build") instanceof Runnable)) )
+ form_error_set.add("missing_required_build_code");
+
+ if( "leaf".equals(node.get("type")) ){
+ if( node.containsKey("neighbor") ) form_error_set.add("leaf_given_neighbor_property");
+ if( node.containsKey("build") ) form_error_set.add("leaf_given_build_property");
+ }
+
+ return form_error_set;
+ }
+
+ // given a node, potentially marks it as wellformed, returns one of 'wellformed' or 'malformed'
+ public static Token wellformed_mark_node(Node node ,boolean verbose){
+ if(debug){
+ if(node != null){
+ System.out.println("wellformed_mark_node::node: " + node);
+ }else{
+ System.out.println("wellformed_mark_node given a null node");
+ }
+ }
+
+ TokenSet form_errors = wellformed_node_q(node);
+ if( form_errors.isEmpty() ){
+ set_mark( node ,"wellformed" );
+ return "wellformed";
+ }
+
+ // At this point we know that form_errors is not empty
+ if(verbose){
+ if( node != null && node.get("label") != null && ((Label)node.get("label")).length() > 0 ){
+ System.out.print( "node " + node.get("label") + " is malformed due to:" );
+ }else{
+ System.out.print("anonymous node is malformed due to:");
+ }
+ for(Token error : form_errors){
+ System.out.print(" " + error);
+ }
+ System.out.println("");
+ }
+
+ return "malformed";
+ }
+ public Token wellformed_mark_node(Node node){
+ return wellformed_mark_node(node ,true);
+ }
+
+ // given a node_label, potentially marks the corresponding node as 'wellformed', returns a token set.
+ // Tokens included "undefined_node", "malformed", and "defactor_leaf".
+ public TokenSet wellformed_mark_node_label(Label node_label ,boolean verbose){
+ TokenSet ret_value = new HashSet<>();
+ Node node = lookup(node_label);
+ if(node == null){
+ ret_value.add("undefined_node");
+ return ret_value;
+ }
+ if( "malformed".equals(wellformed_mark_node(node ,verbose)) ){
+ ret_value.add("malformed");
+ }
+ if( ((List<?>)node.get("neighbor")).isEmpty() ){
+ ret_value.add("defacto_leaf"); // might not be `type:leaf`
+ }
+ return ret_value;
+ }
+ public TokenSet wellformed_mark_node_label(Label node_label){
+ return wellformed_mark_node_label(node_label ,true);
+ }
+
+
+ /*--------------------------------------------------------------------------------
+ A well formed graph checker. Traverses entire graph and marks nodes
+ that are not well formed or that are part of a cycle.
+
+ This must be run on the graph for `lookup_marked_good` to work.
+
+ Each node_label must be a string and not empty.
+
+ Subleties here because we have not yet determined if the nodes we are
+ wellformed (after all ,that is what we are determining here).
+
+ If we want to attempt to build 'islands' of things that might be located on
+ the far side of cycles ,then modify the cycle finder to return a list of
+ cycles (i.e. a list of lists) ,then use each of cycle definition (a list) as
+ the root nodes for further search.
+
+ `path_stack` is a stack of LabelList. The first entry is a clone of the list of
+ root nodes, referenced by label. Each subsequent list is a clone of the
+ neighbor list of the leftmost node of the prior entry.
+
+ `path` is a list of the left most nodes, referenced by label, of the entries
+ on the path stack. This is the path to our current location in the tree.
+ */
+
+
+ private boolean find_and_remove_cycle(List<LabelList> path_stack ,LabelList path ,boolean verbose){
+
+ if( path.size() <= 1 ) return false; // 0 or 1 length path can't have a cycle
+
+ // we want to know if the most recent node added to the path occurs at a point earlier
+ // in the path.
+ int rightmost_index = path.size() - 1;
+ Label recent_node_label = path.get( rightmost_index );
+ int cycle_start_index = path.indexOf(recent_node_label);
+ if( cycle_start_index == -1 ){
+ System.err.println("find_and_remove_cycle:: indexOf does not find index of known list member");
+ return false;
+ }
+ Boolean has_cycle = cycle_start_index < rightmost_index;
+ if(!has_cycle) return false;
+
+ if(verbose) System.out.print("mark_form_graph_descend:: dependency cycle found:");
+ for( Label cycle_node_label : path.subList(cycle_start_index ,path.size()) ){
+ if(verbose) System.out.print(" " + cycle_node_label);
+ Node cycle_node = lookup(cycle_node_label);
+ if( cycle_node.get("mark") == null ){
+ cycle_node.put( "mark" ,new HashTokenSet() );
+ }
+ ( (TokenSet)cycle_node.get("mark") ).add("cycle_member");
+ }
+ if(verbose) System.out.println("");
+
+ // We cannot continue searching after the loop, so we pop back to treat
+ // the first node in the loop as though a leaf node.
+ path_stack.subList( cycle_start_index + 1 ,path_stack.size() ).clear();
+
+ return true;
+ }
+
+ private static TokenSet mark_form_graph_descend_set = new TokenSet();
+ static {
+ mark_form_graph_descend_set.add(new Token("empty_path_stack"));
+ mark_form_graph_descend_set.add(new Token("cycle_found"));
+ mark_form_graph_descend_set.add(new Token("undefined_node"));
+ mark_form_graph_descend_set.add(new Token("exists_malformed"));
+ mark_form_graph_descend_set.add(new Token("defacto_leaf"));
+ }
+
+ private TokenSet mark_form_graph_descend( List<LabelList> path_stack ,boolean verbose ){
+ TokenSet ret_value = new HashSet<>();
+ if(path_stack.isEmpty()){
+ if(verbose) System.out.println( "mark_form_graph_descend:: given empty path_stack to descend from" );
+ ret_value.add( "empty_path_stack" );
+ return ret_value;
+ }
+
+ LabelList local_path = new ArrayList<>();
+ for(LabelList path : path_stack){
+ local_path.add( path.get(0) );
+ }
+ Label local_node_label = local_path.get( local_path.size() - 1 );
+
+ do{
+
+ if( find_and_remove_cycle(path_stack ,local_path ,verbose) ){
+ ret_value.add("cycle_found");
+ return ret_value;
+ }
+
+ TokenSet wellformed_mark_node_label_result = wellformed_mark_node_label(local_node_label ,verbose);
+ ret_value.addAll( wellformed_mark_node_label_result );
+ if(
+ wellformed_mark_node_label_result.contains("undefined_node")
+ || wellformed_mark_node_label_result.contains("defacto_leaf")
+ ){
+ return ret_value;
+ }
+
+ // Descend further into the tree.
+ path_stack.add( new ArrayList<>((LabelList) lookup(local_node_label).get("neighbor")) );
+ local_node_label = (LabelList)lookup(local_node_label).get("neighbor").get(0);
+ local_path.add(local_node_label);
+
+ }while(true);
+ }
+
+
+ /*
+ Given root_node_label_list, marks up the graph and returns a set possibly
+ containing 'all_wellformed' and 'cycles_exist'.
+
+ Marks potentially added to each node include 'cycle_member' and 'wellformed'.
+ Note that these marks are independent.
+ */
+ public TokenSet mark_form_graph(LabelList root_node_label_list ,boolean verbose){
+ TokenSet ret_value = new HashSet<>();
+ boolean exists_malformed = false;
+ TokenSet result; // used variously
+
+ if( root_node_label_list.isEmpty() ) return ret_value;
+
+ // Initialize the DFS tree iterator.
+ List<LabelList> path_stack = new ArrayList<>();
+ path_stack.add( new ArrayList<>(root_node_label_list) );
+
+ // iterate over left side tree descent, not ideal as it starts at the
+ // root each time, but avoids complexity in the cycle detection logic.
+ do{
+ result = mark_form_graph_descend(path_stack ,verbose);
+ if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
+ if( result.contains("undefined_node") ) exists_malformed = true;
+ if( result.contains("exists_malformed") ) exists_malformed = true;
+
+ // increment the iterator to the next leftmost path
+ LabelList top_list = path_stack.get( path_stack.size() - 1 );
+ top_list.remove(0);
+ if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
+
+ }while( !path_stack.isEmpty() );
+
+ if( !exists_malformed ) ret_value.add("all_wellformed");
+
+ if( verbose ){
+ if( exists_malformed ) System.out.println("one or more malformed nodes were found");
+ boolean exists_cycle = ret_value.contains("cycle_exists");
+ if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
+ if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
+ }
+
+ return ret_value;
+ }
+ public TokenSet mark_form_graph(LabelList root_node_label_list){
+ return mark_form_graph(root_node_label_list ,true);
+ }
+
+
+ /*--------------------------------------------------------------------------------
+ Graph traversal
+ */
+
+ // Given a node label, looks it up in the dependency graph, returns the node or null
+ public Node lookup(Label node_label ,boolean verbose){
+ if(node_label == null || node_label.isEmpty()){
+ if(verbose) System.out.println("lookup:: given node_label is null or an empty string");
+ return null;
+ }
+
+ // Try the map
+ Node node = this.node_map.get(node_label);
+ if(node != null){
+ node.put("label" ,node_label);
+ if(verbose) System.out.println("lookup:: found from map: " + node);
+ return node;
+ }
+ // At this point, node will be null
+
+ // The map lookup failed, let's try the function recognizer list
+ Node match_result = null;
+ for (RecognizerF func : this.recognizer_f_list){
+ Node match_result = func.apply(node_label);
+ if("matched".equals(match_result.get("status"))){
+ node = match_result;
+ break;
+ }
+ }
+
+ if(verbose){
+ if(node != null) System.out.println("lookup:: found from recognizer function: " + node);
+ else System.out.println("lookup:: failed to find label: " + node_label);
+ }
+
+ return node;
+ }
+ public Node lookup(Label node_label){
+ return lookup(node_label ,true);
+ }
+
+ // Mark aware lookup function
+ public Node lookup_marked_good(Label node_label ,boolean verbose){
+ Node node = lookup(node_label ,verbose);
+ if(node != null && marked_good_q(node)) return node;
+ return null;
+ }
+ public Node lookup_marked_good(Label node_label){
+ return lookup_marked_good(node_label ,true);
+ }
+
+ /*
+ Given `root_node_label_list` of a DAG, applies `node_function` to each node in a
+ depth-first traversal order. Returns a set of error tokens encountered
+ during traversal.
+
+ `mark_form_graph` must be run on the DAG before this function is called, or
+ `lookup_marked_good` will not function correctly.
+ */
+ public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function ,boolean verbose){
+ if(verbose) System.out.println("all_DAG_DF::");
+
+ TokenSet error_token_set = new HashSet<>();
+
+ boolean accept_arg_list = true;
+ if(node_function == null){
+ error_token_set.add("null_node_function");
+ accept_arg_list = false;
+ }
+ if(!(node_function instanceof BiFunction)){
+ error_token_set.add("node_function_not_a_function");
+ accept_arg_list = false;
+ }
+ if(root_node_label_list == null){
+ error_token_set.add("null_root_node_label_list");
+ accept_arg_list = false;
+ }
+ if(root_node_label_list.isEmpty()){
+ error_token_set.add("empty_root_node_label_list");
+ accept_arg_list = false;
+ }
+ if(!accept_arg_list) return error_token_set;
+
+ TokenSet visited = new HashSet<>();
+ List<Node> in_traversal_order = new ArrayList<>();
+
+ Stack<Label> stack = new Stack<>();
+ root_node_label_list.forEach(stack::push);
+
+ while( !stack.isEmpty() ){
+ Label node_label = stack.pop();
+
+ Node node = lookup_marked_good(node_label ,verbose);
+ if(node == null){
+ error_token_set.add("lookup_fail");
+ continue;
+ }
+
+ if(visited.contains(node.get("label"))) continue;
+ visited.add((Label)node.get("label"));
+
+ in_traversal_order.add(node);
+
+ stack.addAll( (LabelList)node.get("neighbor") );
+ }
+
+ Collections.reverse(in_traversal_order);
+ for(Node node : in_traversal_order){
+ node_function.apply(node ,error_token_set);
+ }
+
+ return error_token_set;
+ }
+ public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function){
+ return all_DAG_DF(root_node_label_list ,node_function ,true);
+ }
+
+ /*--------------------------------------------------------------------------------
+ run the build scripts
+ depends upon is_acyclic having already marked up the graph.
+ */
+
+ // A dependency is "good" if it is marked good, and for leaf or path, if the
+ // corresponding file exists
+ public boolean good_dependency_q(LabelList node_labels){
+ return node_labels.stream().allMatch(node_label -> {
+ Node node = lookup_marked_good(node_label);
+ if( node == null ) return false;
+ if(
+ ("path".equals(node.get("type")) || "leaf".equals(node.get("type")) )
+ && !file_exists_q( (Label) node.get("label") )
+ ){
+ return false;
+ }
+ return true;
+ });
+ }
+
+ /*
+ Given a node label and a list of node labels, returns true if the file at the
+ node label in the first argument is newer than all the files at the
+ corresponding node labels in the second list.
+ */
+ public boolean newer_than_all(Label node_label ,LabelList node_label_list) throws IOException {
+ Path node_path = Paths.get(node_label);
+ if (!Files.exists(node_path)) return false;
+
+ long node_last_modified = Files.getLastModifiedTime(node_path).toMillis();
+
+ return node_label_list.stream().allMatch(label -> {
+ try {
+ Path path = Paths.get(label);
+ if (!Files.exists(path)) return false;
+ long last_modified = Files.getLastModifiedTime(path).toMillis();
+ return node_last_modified > last_modified;
+ } catch (IOException e){
+ return false;
+ }
+ });
+ }
+
+ public boolean can_be_built_q(Node node){
+ if( !marked_good_q(node) ) return false;
+ if(
+ ( "symbol".equals(node.get("type")) || "path".equals(node.get("type")) )
+ && !good_dependency_q( (LabelList)node.get("neighbor") )
+ ){
+ return false;
+ }
+ if(
+ "leaf".equals( node.get("type") )
+ && !file_exists_q( (Label)node.get("label") )
+ ){
+ return false;
+ }
+ return true;
+ }
+
+ // `can_be_build_q` must be true for this to be meaningful:
+ public boolean should_be_built_q(Node node ,boolean verbose) throws IOException {
+ if ("leaf".equals(node.get("type"))) return false;
+ if ("symbol".equals(node.get("type"))) return true;
+ if ("path".equals(node.get("type"))) return !newer_than_all((Label) node.get("label") ,(LabelList) node.get("neighbor"));
+
+ if (verbose){
+ System.out.println("should_be_build_q:: unrecognized node type, so assuming it should not be built.");
+ }
+ return false;
+ }
+ public boolean should_be_built_q(Node node) throws IOException {
+ return should_be_built_q(node ,true);
+ }
+
+ /*
+ Runs the build scripts, assuming the graph has been marked up already.
+ */
+ public void run_build_scripts_f(LabelList root_node_label_list ,boolean verbose) throws IOException {
+
+ if(root_node_label_list.isEmpty()) return;
+
+ TokenSet error_token_set = new HashSet<>(); // used to catch return values
+
+ System.out.println("run_build_script:: Checking if graph is well formed.");
+ error_token_set = mark_form_graph(root_node_label_list);
+ if(error_token_set != null && !error_token_set.isEmpty()){
+ System.out.println("Graph is not well-formed. Expect build problems. Errors:");
+ error_token_set.forEach(token -> System.out.println(" - " + token));
+ } else {
+ System.out.println("Graph is well-formed. Proceeding with build.");
+ }
+
+ // Define the node function
+ BiConsumer<Node ,TokenSet> node_function = (node ,error_token_set_2) -> {
+ if(!can_be_built_q(node)){
+ System.out.println("run_build_scripts_f:: Skipping build for " + node.get("label") + " due to problems with dependencies.");
+ return;
+ }
+ if(!should_be_built_q(node)){
+ if(verbose) System.out.println("run_build_scripts_f:: " + node.get("label") + " already up to date");
+ return;
+ }
+
+ // Build the target
+ System.out.println("run_build_scripts_f:: Running build script for " + node.get("label"));
+ // Assuming node.build() is a method in the Map or a related object
+ // Replace this with the actual build function for the node
+ // node.build();
+
+ // For path nodes, check if the build updated the target path
+ if("path".equals(node.get("type")) && should_be_built_q(node)){
+ System.out.println("run_build_scripts_f:: Build failed for " + node.get("label"));
+ set_mark(node ,"build_failed");
+ }
+ };
+
+ System.out.println("run_build_scripts_f:: running ...");
+ error_token_set = all_DAG_DF(root_node_label_list ,node_function ,verbose);
+ if(error_token_set != null){
+ error_token_set.forEach(error -> System.out.println("run_build_scripts_f::all_DAG_DF:: " + error));
+ }
+ }
+ public void run_build_scripts_f(LabelList root_node_label_list) throws IOException {
+ run_build_scripts_f(root_node_label_list ,true);
+ }
+
+}
--- /dev/null
+package Ariadne;
+
+/*
+A node label.
+
+*/
+public class Label {
+ private final String value;
+
+ public Label(String value) {
+ this.value = value;
+ }
+
+ public String get() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Label label = (Label) o;
+ return value.equals(label.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
--- /dev/null
+package Ariadne;
+
+/*
+An error token.
+
+*/
+public class Token {
+ private final String value;
+
+ public Token(String value) {
+ this.value = value;
+ }
+
+ public String get() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Token token = (Token) o;
+ return value.equals(token.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
--- /dev/null
+package Ariadne;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+
+public class File {
+ static boolean debug = false;
+
+ public static Map<String, String> unpack_file_path(String file_fp) {
+ if (debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
+
+ // Use java.io.File explicitly to avoid conflict with the custom File class
+ java.io.File file = new java.io.File(file_fp);
+ String parent_dp = (file.getParent() != null) ? file.getParent() : "";
+
+ if (!parent_dp.isEmpty() && !parent_dp.endsWith(java.io.File.separator)) {
+ parent_dp += java.io.File.separator;
+ }
+
+ String file_fn = file.getName();
+ String file_fn_base = file_fn;
+ String file_fn_ext = "";
+
+ int last_index = file_fn.lastIndexOf('.');
+ if (last_index > 0) {
+ file_fn_base = file_fn.substring(0, last_index);
+ if (last_index + 1 < file_fn.length()) {
+ file_fn_ext = file_fn.substring(last_index + 1);
+ }
+ }
+
+ Map<String, String> ret_val = new HashMap<>();
+ ret_val.put("dp", parent_dp);
+ ret_val.put("fn", file_fn);
+ ret_val.put("fn_base", file_fn_base);
+ ret_val.put("fn_ext", file_fn_ext);
+
+ if (debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
+
+ return ret_val;
+ }
+
+ public static boolean file_exists_q(String fp_string) {
+ Path fp_object = Paths.get(fp_string);
+ return Files.exists(fp_object);
+ }
+
+ /*
+ Given a target_fp and a list of list of dependency_fp.
+
+ Returns false if either the target is newer than all the dependencies, or one
+ of the specified files is missing. Otherwise returns true.
+ */
+ public static boolean newer_than_all(String target_fp_string, List<String> dependency_fp_list) throws IOException {
+ Path target_fp_object = Paths.get(target_fp_string);
+ if (!Files.exists(target_fp_object)) return false;
+
+ long target_last_modified_time = Files.getLastModifiedTime(target_fp_object).toMillis();
+
+ return dependency_fp_list.stream().allMatch(dependency_fp -> {
+ try {
+ Path dependency_fp_object = Paths.get(dependency_fp);
+ if (!Files.exists(dependency_fp_object)) return false;
+ long dependency_last_modified_time = Files.getLastModifiedTime(dependency_fp_object).toMillis();
+ return target_last_modified_time > dependency_last_modified_time;
+ } catch (IOException e) {
+ return false;
+ }
+ });
+ }
+
+}
+++ /dev/null
-package Ariadne;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-
-public class Graph {
-
- /*--------------------------------------------------------------------------------
- type aliases
- */
- public class TokenSet extends HashSet<Token>{}
- public class LabelList extends ArrayList<Label> {}
- public interface Node extends Map<Label ,Object>{}
- public interface NodeList extends List<Node>{}
- public interface RecognizerF extends Function<Label ,Node>{}
- public class RecognizerFList extends ArrayList<RecognizerF> {}
-
- /*--------------------------------------------------------------------------------
- instance data
- */
-
- private static boolean debug = true;
- private Map<Label ,Node> node_map;
- private RecognizerFList recognizer_f_list;
-
- /*--------------------------------------------------------------------------------
- constructors
- */
-
- public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
- if (node_map == null && recognizer_f_list == null) {
- System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
- System.exit(1);
- }
-
- // Initialize node_map and recognizer_f_list to empty collections if they are null
- this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
-
- this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
- }
-
- /*--------------------------------------------------------------------------------
- file utilities
- */
-
- public static Map<String ,String> unpack_file_path(String file_fp){
- if(debug) System.out.println("unpack_file_path::file_fp: " + file_fp);
-
- File file = new File(file_fp);
- String parent_dp = (file.getParent() != null) ? file.getParent() : "";
-
- if( !parent_dp.isEmpty() && !parent_dp.endsWith(File.separator) ){
- parent_dp += File.separator;
- }
-
- String file_fn = file.getName();
- String file_fn_base = file_fn;
- String file_fn_ext = "";
-
- int last_index = file_fn.lastIndexOf('.');
- if(last_index > 0){
- file_fn_base = file_fn.substring(0 ,last_index);
- if( last_index + 1 < file_fn.length() ){
- file_fn_ext = file_fn.substring(last_index + 1);
- }
- }
-
- Map<String ,String> ret_val = new HashMap<>();
- ret_val.put("dp" ,parent_dp);
- ret_val.put("fn" ,file_fn);
- ret_val.put("fn_base" ,file_fn_base);
- ret_val.put("fn_ext" ,file_fn_ext);
-
- if(debug) System.out.println("unpack_file_path::ret_val: " + ret_val);
-
- return ret_val;
- }
-
- public static boolean file_exists_q(Label node_label){
- Path node_path = Paths.get(node_label);
- return Files.exists(node_path);
- }
-
- /*--------------------------------------------------------------------------------
- About nodes
-
- A leaf type node specifies a path to a file that should not be deleted by
- in clean operations. Typically this is the source code. We could add a
- tool to lock permissions on these before a build, so that the build
- scripts will also not mess with them (unless they change permissions).
-
- If the user has multiple dependency graphs defined, a node with no
- dependencies in one graph, might have dependencies in another.
-
- An error type node, is one that was found to not have a type, or
- was constructed by the tool to be a place older, perhaps for a
- node label that was not found.
-
- */
- public static TokenSet all_node_type_set = new TokenSet();
- static {
- all_node_type_set.add(new Token("symbol"));
- all_node_type_set.add(new Token("path"));
- all_node_type_set.add(new Token("leaf"));
- all_node_type_set.add(new Token("error"));
- }
-
- public static TokenSet persistent_node_mark_set = new TokenSet();
- static {
- persistent_node_mark_set.add(new Token("cycle_member"));
- persistent_node_mark_set.add(new Token("wellformed"));
- persistent_node_mark_set.add(new Token("build_failed"));
- persistent_node_mark_set.add(new Token("null_node"));
- }
-
- public static boolean leaf_q(Node node){
- return node != null && "leaf".equals(node.get("type"));
- }
-
- public static boolean has_mark(Node node){
- return node != null && node.get("mark") != null && !( (TokenSet)node.get("mark") ).isEmpty();
- }
-
- public static void set_mark(Node node ,Token mark){
- if( node.get("mark") == null ){
- node.put("mark" ,new HashTokenSet());
- }
- ( (TokenSet)node.get("mark") ).add(mark);
- }
-
- public static void clear_mark(Node node ,Token mark){
- if( node != null && node.get("mark") != null ){
- ( (TokenSet) node.get("mark") ).remove(mark);
- }
- }
-
- public static boolean marked_good_q(Node node){
- return node != null && node.get("mark") != null
- && ( (TokenSet)node.get("mark") ).contains("wellformed")
- && !( (TokenSet)node.get("mark") ).contains("cycle_member")
- && !( (TokenSet)node.get("mark") ).contains("build_failed");
- }
-
-
- /*--------------------------------------------------------------------------------
- Well-formed Node Check
- */
-
- public static TokenSet form_condition_set = new TokenSet();
- static {
- form_condition_set.add(new Token("no_node"));
- form_condition_set.add(new Token("node_must_have_label"));
- form_condition_set.add(new Token("label_must_be_string"));
- form_condition_set.add(new Token("node_must_have_type"));
- form_condition_set.add(new Token("bad_node_type"));
- form_condition_set.add(new Token("neighbor_value_must_be_list"));
- form_condition_set.add(new Token("neighbor_reference_must_be_string"));
- form_condition_set.add(new Token("neighbor_label_not_in_graph"));
- form_condition_set.add(new Token("mark_property_value_must_be_set"));
- form_condition_set.add(new Token("unregistered_mark"));
- form_condition_set.add(new Token("missing_required_build_code"));
- form_condition_set.add(new Token("leaf_given_neighbor_property"));
- form_condition_set.add(new Token("leaf_given_build_property"));
- }
-
- // given a node, collects a description of its form, returns a set form condition tokens
- public static TokenSet wellformed_node_q(Node node){
- TokenSet form_error_set = new HashSet<>();
-
- if(node == null){
- form_error_set.add("null_node");
- return form_error_set;
- }
-
- if( !node.containsKey("label") )
- form_error_set.add("node_must_have_label");
- else if( !(node.get("label") instanceof Label) )
- form_error_set.add("label_must_be_string");
-
- if( !node.containsKey("type") )
- form_error_set.add("node_must_have_type");
- else if( !(node.get("type") instanceof String) || !all_node_type_set.contains(node.get("type")) )
- form_error_set.add("bad_node_type");
-
- if( node.containsKey("neighbor") ){
- if( !(node.get("neighbor") instanceof List) )
- form_error_set.add("neighbor_value_must_be_list");
- else if( !((List<?>) node.get("neighbor")).stream().allMatch(it -> it instanceof Label) )
- form_error_set.add("neighbor_reference_must_be_string");
- }
-
- if( node.containsKey("mark") ){
- if( !(node.get("mark") instanceof Set) )
- form_error_set.add("mark_property_value_must_be_set");
- else if( !((Set<?>) node.get("mark")).stream().allMatch(it -> persistent_node_mark_set.contains(it)) )
- form_error_set.add("unregistered_mark");
- }
-
- if( "path".equals(node.get("type")) && (!node.containsKey("build") || !(node.get("build") instanceof Runnable)) )
- form_error_set.add("missing_required_build_code");
-
- if( "leaf".equals(node.get("type")) ){
- if( node.containsKey("neighbor") ) form_error_set.add("leaf_given_neighbor_property");
- if( node.containsKey("build") ) form_error_set.add("leaf_given_build_property");
- }
-
- return form_error_set;
- }
-
- // given a node, potentially marks it as wellformed, returns one of 'wellformed' or 'malformed'
- public static Token wellformed_mark_node(Node node ,boolean verbose){
- if(debug){
- if(node != null){
- System.out.println("wellformed_mark_node::node: " + node);
- }else{
- System.out.println("wellformed_mark_node given a null node");
- }
- }
-
- TokenSet form_errors = wellformed_node_q(node);
- if( form_errors.isEmpty() ){
- set_mark( node ,"wellformed" );
- return "wellformed";
- }
-
- // At this point we know that form_errors is not empty
- if(verbose){
- if( node != null && node.get("label") != null && ((Label)node.get("label")).length() > 0 ){
- System.out.print( "node " + node.get("label") + " is malformed due to:" );
- }else{
- System.out.print("anonymous node is malformed due to:");
- }
- for(Token error : form_errors){
- System.out.print(" " + error);
- }
- System.out.println("");
- }
-
- return "malformed";
- }
- public Token wellformed_mark_node(Node node){
- return wellformed_mark_node(node ,true);
- }
-
- // given a node_label, potentially marks the corresponding node as 'wellformed', returns a token set.
- // Tokens included "undefined_node", "malformed", and "defactor_leaf".
- public TokenSet wellformed_mark_node_label(Label node_label ,boolean verbose){
- TokenSet ret_value = new HashSet<>();
- Node node = lookup(node_label);
- if(node == null){
- ret_value.add("undefined_node");
- return ret_value;
- }
- if( "malformed".equals(wellformed_mark_node(node ,verbose)) ){
- ret_value.add("malformed");
- }
- if( ((List<?>)node.get("neighbor")).isEmpty() ){
- ret_value.add("defacto_leaf"); // might not be `type:leaf`
- }
- return ret_value;
- }
- public TokenSet wellformed_mark_node_label(Label node_label){
- return wellformed_mark_node_label(node_label ,true);
- }
-
-
- /*--------------------------------------------------------------------------------
- A well formed graph checker. Traverses entire graph and marks nodes
- that are not well formed or that are part of a cycle.
-
- This must be run on the graph for `lookup_marked_good` to work.
-
- Each node_label must be a string and not empty.
-
- Subleties here because we have not yet determined if the nodes we are
- wellformed (after all ,that is what we are determining here).
-
- If we want to attempt to build 'islands' of things that might be located on
- the far side of cycles ,then modify the cycle finder to return a list of
- cycles (i.e. a list of lists) ,then use each of cycle definition (a list) as
- the root nodes for further search.
-
- `path_stack` is a stack of LabelList. The first entry is a clone of the list of
- root nodes, referenced by label. Each subsequent list is a clone of the
- neighbor list of the leftmost node of the prior entry.
-
- `path` is a list of the left most nodes, referenced by label, of the entries
- on the path stack. This is the path to our current location in the tree.
- */
-
-
- private boolean find_and_remove_cycle(List<LabelList> path_stack ,LabelList path ,boolean verbose){
-
- if( path.size() <= 1 ) return false; // 0 or 1 length path can't have a cycle
-
- // we want to know if the most recent node added to the path occurs at a point earlier
- // in the path.
- int rightmost_index = path.size() - 1;
- Label recent_node_label = path.get( rightmost_index );
- int cycle_start_index = path.indexOf(recent_node_label);
- if( cycle_start_index == -1 ){
- System.err.println("find_and_remove_cycle:: indexOf does not find index of known list member");
- return false;
- }
- Boolean has_cycle = cycle_start_index < rightmost_index;
- if(!has_cycle) return false;
-
- if(verbose) System.out.print("mark_form_graph_descend:: dependency cycle found:");
- for( Label cycle_node_label : path.subList(cycle_start_index ,path.size()) ){
- if(verbose) System.out.print(" " + cycle_node_label);
- Node cycle_node = lookup(cycle_node_label);
- if( cycle_node.get("mark") == null ){
- cycle_node.put( "mark" ,new HashTokenSet() );
- }
- ( (TokenSet)cycle_node.get("mark") ).add("cycle_member");
- }
- if(verbose) System.out.println("");
-
- // We cannot continue searching after the loop, so we pop back to treat
- // the first node in the loop as though a leaf node.
- path_stack.subList( cycle_start_index + 1 ,path_stack.size() ).clear();
-
- return true;
- }
-
- private static TokenSet mark_form_graph_descend_set = new TokenSet();
- static {
- mark_form_graph_descend_set.add(new Token("empty_path_stack"));
- mark_form_graph_descend_set.add(new Token("cycle_found"));
- mark_form_graph_descend_set.add(new Token("undefined_node"));
- mark_form_graph_descend_set.add(new Token("exists_malformed"));
- mark_form_graph_descend_set.add(new Token("defacto_leaf"));
- }
-
- private TokenSet mark_form_graph_descend( List<LabelList> path_stack ,boolean verbose ){
- TokenSet ret_value = new HashSet<>();
- if(path_stack.isEmpty()){
- if(verbose) System.out.println( "mark_form_graph_descend:: given empty path_stack to descend from" );
- ret_value.add( "empty_path_stack" );
- return ret_value;
- }
-
- LabelList local_path = new ArrayList<>();
- for(LabelList path : path_stack){
- local_path.add( path.get(0) );
- }
- Label local_node_label = local_path.get( local_path.size() - 1 );
-
- do{
-
- if( find_and_remove_cycle(path_stack ,local_path ,verbose) ){
- ret_value.add("cycle_found");
- return ret_value;
- }
-
- TokenSet wellformed_mark_node_label_result = wellformed_mark_node_label(local_node_label ,verbose);
- ret_value.addAll( wellformed_mark_node_label_result );
- if(
- wellformed_mark_node_label_result.contains("undefined_node")
- || wellformed_mark_node_label_result.contains("defacto_leaf")
- ){
- return ret_value;
- }
-
- // Descend further into the tree.
- path_stack.add( new ArrayList<>((LabelList) lookup(local_node_label).get("neighbor")) );
- local_node_label = (LabelList)lookup(local_node_label).get("neighbor").get(0);
- local_path.add(local_node_label);
-
- }while(true);
- }
-
-
- /*
- Given root_node_label_list, marks up the graph and returns a set possibly
- containing 'all_wellformed' and 'cycles_exist'.
-
- Marks potentially added to each node include 'cycle_member' and 'wellformed'.
- Note that these marks are independent.
- */
- public TokenSet mark_form_graph(LabelList root_node_label_list ,boolean verbose){
- TokenSet ret_value = new HashSet<>();
- boolean exists_malformed = false;
- TokenSet result; // used variously
-
- if( root_node_label_list.isEmpty() ) return ret_value;
-
- // Initialize the DFS tree iterator.
- List<LabelList> path_stack = new ArrayList<>();
- path_stack.add( new ArrayList<>(root_node_label_list) );
-
- // iterate over left side tree descent, not ideal as it starts at the
- // root each time, but avoids complexity in the cycle detection logic.
- do{
- result = mark_form_graph_descend(path_stack ,verbose);
- if( result.contains("cycle_found") ) ret_value.add("cycle_exists");
- if( result.contains("undefined_node") ) exists_malformed = true;
- if( result.contains("exists_malformed") ) exists_malformed = true;
-
- // increment the iterator to the next leftmost path
- LabelList top_list = path_stack.get( path_stack.size() - 1 );
- top_list.remove(0);
- if( top_list.isEmpty() ) path_stack.remove( path_stack.size() - 1 );
-
- }while( !path_stack.isEmpty() );
-
- if( !exists_malformed ) ret_value.add("all_wellformed");
-
- if( verbose ){
- if( exists_malformed ) System.out.println("one or more malformed nodes were found");
- boolean exists_cycle = ret_value.contains("cycle_exists");
- if( exists_cycle ) System.out.println("one or more cyclic dependency loop found");
- if( exists_malformed || exists_cycle ) System.out.println("will attempt to build unaffected nodes");
- }
-
- return ret_value;
- }
- public TokenSet mark_form_graph(LabelList root_node_label_list){
- return mark_form_graph(root_node_label_list ,true);
- }
-
-
- /*--------------------------------------------------------------------------------
- Graph traversal
- */
-
- // Given a node label, looks it up in the dependency graph, returns the node or null
- public Node lookup(Label node_label ,boolean verbose){
- if(node_label == null || node_label.isEmpty()){
- if(verbose) System.out.println("lookup:: given node_label is null or an empty string");
- return null;
- }
-
- // Try the map
- Node node = this.node_map.get(node_label);
- if(node != null){
- node.put("label" ,node_label);
- if(verbose) System.out.println("lookup:: found from map: " + node);
- return node;
- }
- // At this point, node will be null
-
- // The map lookup failed, let's try the function recognizer list
- Node match_result = null;
- for (RecognizerF func : this.recognizer_f_list){
- Node match_result = func.apply(node_label);
- if("matched".equals(match_result.get("status"))){
- node = match_result;
- break;
- }
- }
-
- if(verbose){
- if(node != null) System.out.println("lookup:: found from recognizer function: " + node);
- else System.out.println("lookup:: failed to find label: " + node_label);
- }
-
- return node;
- }
- public Node lookup(Label node_label){
- return lookup(node_label ,true);
- }
-
- // Mark aware lookup function
- public Node lookup_marked_good(Label node_label ,boolean verbose){
- Node node = lookup(node_label ,verbose);
- if(node != null && marked_good_q(node)) return node;
- return null;
- }
- public Node lookup_marked_good(Label node_label){
- return lookup_marked_good(node_label ,true);
- }
-
- /*
- Given `root_node_label_list` of a DAG, applies `node_function` to each node in a
- depth-first traversal order. Returns a set of error tokens encountered
- during traversal.
-
- `mark_form_graph` must be run on the DAG before this function is called, or
- `lookup_marked_good` will not function correctly.
- */
- public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function ,boolean verbose){
- if(verbose) System.out.println("all_DAG_DF::");
-
- TokenSet error_token_set = new HashSet<>();
-
- boolean accept_arg_list = true;
- if(node_function == null){
- error_token_set.add("null_node_function");
- accept_arg_list = false;
- }
- if(!(node_function instanceof BiFunction)){
- error_token_set.add("node_function_not_a_function");
- accept_arg_list = false;
- }
- if(root_node_label_list == null){
- error_token_set.add("null_root_node_label_list");
- accept_arg_list = false;
- }
- if(root_node_label_list.isEmpty()){
- error_token_set.add("empty_root_node_label_list");
- accept_arg_list = false;
- }
- if(!accept_arg_list) return error_token_set;
-
- TokenSet visited = new HashSet<>();
- List<Node> in_traversal_order = new ArrayList<>();
-
- Stack<Label> stack = new Stack<>();
- root_node_label_list.forEach(stack::push);
-
- while( !stack.isEmpty() ){
- Label node_label = stack.pop();
-
- Node node = lookup_marked_good(node_label ,verbose);
- if(node == null){
- error_token_set.add("lookup_fail");
- continue;
- }
-
- if(visited.contains(node.get("label"))) continue;
- visited.add((Label)node.get("label"));
-
- in_traversal_order.add(node);
-
- stack.addAll( (LabelList)node.get("neighbor") );
- }
-
- Collections.reverse(in_traversal_order);
- for(Node node : in_traversal_order){
- node_function.apply(node ,error_token_set);
- }
-
- return error_token_set;
- }
- public TokenSet all_DAG_DF(LabelList root_node_label_list ,BiConsumer<Node ,TokenSet> node_function){
- return all_DAG_DF(root_node_label_list ,node_function ,true);
- }
-
- /*--------------------------------------------------------------------------------
- run the build scripts
- depends upon is_acyclic having already marked up the graph.
- */
-
- // A dependency is "good" if it is marked good, and for leaf or path, if the
- // corresponding file exists
- public boolean good_dependency_q(LabelList node_labels){
- return node_labels.stream().allMatch(node_label -> {
- Node node = lookup_marked_good(node_label);
- if( node == null ) return false;
- if(
- ("path".equals(node.get("type")) || "leaf".equals(node.get("type")) )
- && !file_exists_q( (Label) node.get("label") )
- ){
- return false;
- }
- return true;
- });
- }
-
- /*
- Given a node label and a list of node labels, returns true if the file at the
- node label in the first argument is newer than all the files at the
- corresponding node labels in the second list.
- */
- public boolean newer_than_all(Label node_label ,LabelList node_label_list) throws IOException {
- Path node_path = Paths.get(node_label);
- if (!Files.exists(node_path)) return false;
-
- long node_last_modified = Files.getLastModifiedTime(node_path).toMillis();
-
- return node_label_list.stream().allMatch(label -> {
- try {
- Path path = Paths.get(label);
- if (!Files.exists(path)) return false;
- long last_modified = Files.getLastModifiedTime(path).toMillis();
- return node_last_modified > last_modified;
- } catch (IOException e){
- return false;
- }
- });
- }
-
- public boolean can_be_built_q(Node node){
- if( !marked_good_q(node) ) return false;
- if(
- ( "symbol".equals(node.get("type")) || "path".equals(node.get("type")) )
- && !good_dependency_q( (LabelList)node.get("neighbor") )
- ){
- return false;
- }
- if(
- "leaf".equals( node.get("type") )
- && !file_exists_q( (Label)node.get("label") )
- ){
- return false;
- }
- return true;
- }
-
- // `can_be_build_q` must be true for this to be meaningful:
- public boolean should_be_built_q(Node node ,boolean verbose) throws IOException {
- if ("leaf".equals(node.get("type"))) return false;
- if ("symbol".equals(node.get("type"))) return true;
- if ("path".equals(node.get("type"))) return !newer_than_all((Label) node.get("label") ,(LabelList) node.get("neighbor"));
-
- if (verbose){
- System.out.println("should_be_build_q:: unrecognized node type, so assuming it should not be built.");
- }
- return false;
- }
- public boolean should_be_built_q(Node node) throws IOException {
- return should_be_built_q(node ,true);
- }
-
- /*
- Runs the build scripts, assuming the graph has been marked up already.
- */
- public void run_build_scripts_f(LabelList root_node_label_list ,boolean verbose) throws IOException {
-
- if(root_node_label_list.isEmpty()) return;
-
- TokenSet error_token_set = new HashSet<>(); // used to catch return values
-
- System.out.println("run_build_script:: Checking if graph is well formed.");
- error_token_set = mark_form_graph(root_node_label_list);
- if(error_token_set != null && !error_token_set.isEmpty()){
- System.out.println("Graph is not well-formed. Expect build problems. Errors:");
- error_token_set.forEach(token -> System.out.println(" - " + token));
- } else {
- System.out.println("Graph is well-formed. Proceeding with build.");
- }
-
- // Define the node function
- BiConsumer<Node ,TokenSet> node_function = (node ,error_token_set_2) -> {
- if(!can_be_built_q(node)){
- System.out.println("run_build_scripts_f:: Skipping build for " + node.get("label") + " due to problems with dependencies.");
- return;
- }
- if(!should_be_built_q(node)){
- if(verbose) System.out.println("run_build_scripts_f:: " + node.get("label") + " already up to date");
- return;
- }
-
- // Build the target
- System.out.println("run_build_scripts_f:: Running build script for " + node.get("label"));
- // Assuming node.build() is a method in the Map or a related object
- // Replace this with the actual build function for the node
- // node.build();
-
- // For path nodes, check if the build updated the target path
- if("path".equals(node.get("type")) && should_be_built_q(node)){
- System.out.println("run_build_scripts_f:: Build failed for " + node.get("label"));
- set_mark(node ,"build_failed");
- }
- };
-
- System.out.println("run_build_scripts_f:: running ...");
- error_token_set = all_DAG_DF(root_node_label_list ,node_function ,verbose);
- if(error_token_set != null){
- error_token_set.forEach(error -> System.out.println("run_build_scripts_f::all_DAG_DF:: " + error));
- }
- }
- public void run_build_scripts_f(LabelList root_node_label_list) throws IOException {
- run_build_scripts_f(root_node_label_list ,true);
- }
-
-}
--- /dev/null
+package Ariadne;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Graph {
+
+ /*--------------------------------------------------------------------------------
+ type aliases
+ */
+ public class TokenSet extends HashSet<Token>{}
+ public class LabelList extends ArrayList<Label> {}
+ public interface Node extends Map<Label ,Object>{}
+ public interface NodeList extends List<Node>{}
+ public interface RecognizerF extends Function<Label ,Node>{}
+ public class RecognizerFList extends ArrayList<RecognizerF> {}
+
+ /*--------------------------------------------------------------------------------
+ constructors
+ */
+ public AriadneGraph(Map<Label ,Node> node_map ,RecognizerFList recognizer_f_list){
+ if (node_map == null && recognizer_f_list == null) {
+ System.err.println("AriadneGraph: At least one of 'node_map' (Map) or 'recognizer_f_list' (List) must be provided.");
+ System.exit(1);
+ }
+
+ // Initialize each of node_map and recognizer_f_list to empty collections if null
+ this.node_map = (node_map != null) ? node_map : new HashMap<Label ,Node>();
+ this.recognizer_f_list = (recognizer_f_list != null) ? recognizer_f_list : new RecognizerFList();
+ }
+
+
+
+ /*--------------------------------------------------------------------------------
+ instance data
+ */
+ private static boolean debug = true;
+ private Map<Label ,Node> node_map;
+ private RecognizerFList recognizer_f_list;
+
+
+ // Internal map to store nodes, with labels as keys
+ private Map<Label, Node> node_map;
+
+ // Constructor to initialize the node map
+ public Graph(Map<Label, Node> node_map) {
+ if (node_map == null) {
+ this.node_map = new HashMap<>();
+ } else {
+ this.node_map = node_map;
+ }
+ }
+
+ // Lookup method to find a node by its label
+ public Node lookup(Label node_label, boolean verbose) {
+ if (node_label == null || node_label.isEmpty()) {
+ if (verbose) {
+ System.out.println("lookup:: given node_label is null or empty.");
+ }
+ return null;
+ }
+
+ // Try to retrieve the node from the map
+ Node node = this.node_map.get(node_label);
+
+ if (verbose) {
+ if (node != null) {
+ System.out.println("lookup:: found node: " + node);
+ } else {
+ System.out.println("lookup:: node not found for label: " + node_label);
+ }
+ }
+
+ return node;
+ }
+
+ // Overloaded lookup method with default verbosity (true)
+ public Node lookup(Label node_label) {
+ return lookup(node_label, true);
+ }
+
+}
+++ /dev/null
-package Ariadne;
-
-/*
-A node label.
-
-*/
-public class Label {
- private final String value;
-
- public Label(String value) {
- this.value = value;
- }
-
- public String get() {
- return value;
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Label label = (Label) o;
- return value.equals(label.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-}
+++ /dev/null
-package Ariadne;
-
-/*
-An error token.
-
-*/
-public class Token {
- private final String value;
-
- public Token(String value) {
- this.value = value;
- }
-
- public String get() {
- return value;
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Token token = (Token) o;
- return value.equals(token.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-}
+++ /dev/null
-#!/bin/env bash
-
-java BuildGraph "$@"
+++ /dev/null
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
- echo "release:: REPO_HOME is not set."
- exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
- echo "release:: ENV_DEV_BUILD is not set."
- exit 1
-fi
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-set +x
-
-echo "clean complete."
+++ /dev/null
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
- echo "release:: REPO_HOME is not set."
- exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
- echo "release:: ENV_DEV_BUILD is not set."
- exit 1
-fi
-
-wrapper=build
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-rm -f jvm/Ariadne.jar
-rm -f shell/{$wrapper}
-set +x
-
-echo "clean_all complete."
+++ /dev/null
-#!/bin/bash
-
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
-if [ -z "$REPO_HOME" ]; then
- echo "release:: REPO_HOME is not set."
- exit 1
-fi
-
-if [ -z "$ENV_DEV_BUILD" ]; then
- echo "release:: ENV_DEV_BUILD is not set."
- exit 1
-fi
-
-# Define release directory
-release_dir="$REPO_HOME"/release_candidate
-wrapper=build
-
-set -x
-cd "$REPO_HOME"/developer
-rm -rf scratch_pad/*
-rm -f jvm/Ariadne.jar
-rm -f shell/"$wrapper"
-rm -f "$release_dir"/Ariadne.jar
-rm -f "$release_dir"/{$wrapper}
-set +x
-
-echo "clean_dist complete."
--- /dev/null
+#!/bin/bash
+# remove all files created by make's call to `javac`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$REPO_HOME" ]; then
+ echo "$script_name:: REPO_HOME is not set."
+ exit 1
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+ echo "release:: ENV_DEV_BUILD is not set."
+ exit 1
+fi
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+set +x
+
+echo "$script_name done."
--- /dev/null
+#!/bin/bash
+# remove all files made by `make`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+error=false
+if [ -z "$REPO_HOME" ]; then
+ echo "$script_name:: REPO_HOME is not set."
+ error=true
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+ echo "$script_name:: ENV_DEV_BUILD is not set."
+ error=true
+fi
+if [ error ]; then
+ exit 1
+fi
+
+wrapper=build
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+rm -f jvm/Ariadne.jar
+rm -f shell/{$wrapper}
+set +x
+
+echo "$script_name done."
--- /dev/null
+#!/bin/bash
+# remove file made by `make` and by `release`
+
+script_name=$(basename ${BASH_SOURCE[0]})
+error=false
+if [ -z "$REPO_HOME" ]; then
+ echo "$script_name:: REPO_HOME is not set."
+ error=true
+fi
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+ echo "$script_name:: ENV_DEV_BUILD is not set."
+ error=true
+fi
+if [ error ]; then
+ exit 1
+fi
+
+# Define release directory
+release_dir="$REPO_HOME"/release_candidate
+wrapper=build
+
+set -x
+cd "$REPO_HOME"/developer
+rm -rf scratch_pad/*
+rm -f jvm/Ariadne.jar
+rm -f shell/"$wrapper"
+rm -f "$release_dir"/Ariadne.jar
+rm -f "$release_dir"/{$wrapper}
+set +x
+
+echo "$script_name done."
#!/usr/bin/env bash
-echo "REOP_HOME/developer/tool/env"
+## input guards
-# The build environment.
-#
-env_error=false
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "env_build:: This script must be sourced, not executed."
- env_error=true
-fi
-if [ -z "$ENV_DEVELOPER" ]; then
- echo "env_build:: script can only be run from the developer environment"
- env_error=true
+# instead of bailing, we will go fetch what we need
+env_should_be="tool_shared/bespoke/env"
+if [ "$ENV" != "$env_should_be" ]; then
+ source "$REPO_HOME"/"$env_should_be"
fi
-if [ "$env_error" = true ]; then
+
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "$script_name:: This script must be sourced, not executed."
exit 1
fi
-# goovy use has been deprecated
-# export GROOVY_HOME="$REPO_HOME/tool_shared/third_party/groovy-4.0.9"
+## so we can do the build
+
+cd "$REPO_HOME"/developer/
+
+export ENV=$(script_fp)
+export PROJECT="$PROJECT"_developer
-# third party tools we will use
-export JAVA_HOME="$REPO_HOME/tool_shared/third_party/jdk-11"
export PATH=\
+"$REPO_HOME"/developer/tool/\
+:"$REPO_HOME"/tool_shared/bespoke/\
"$JAVA_HOME"/bin\
:"$PATH"
-# where the tool build finds its class files:
+## so we can run locally the stuff we built,
+
export CLASSPATH=\
"$REPO_HOME"/developer/jvm\
:"$REPO_HOME"/developer/jvm/Ariadne.jar\
:"$JAVA_HOME"/lib\
:"$CLASSPATH"
-# after we building the tool we might want to run it
-# the shell directory holds the bash UI wrappers
export PATH=\
"$REPO_HOME"/developer/shell\
:"$PATH"
-export ENV_DEV_BUILD=true
-echo "${BASH_SOURCE[0]}" "complete"
+## misc
+
+# make .githolder and .gitignore visible
+alias ls="ls -a"
+
+echo "$ENV complete."
#!/bin/env bash
-if [ -z "$ENV_DEVELOPER" ]; then
- echo "make_PaintIt:: script can only be run from the developer environment"
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$ENV_DEVELOPER_LOCAL" ]; then
+ echo "$script_name:: script can only be run from the developer environment"
return 1
fi
wrapper=build
for file in $wrapper;do
- cat > shell/$file << EOL
- #!/bin/bash
- java com/ReasoningTechnology/Ariadne/$file
- EOL
- chmod +x shell/$file
+
+cat > shell/$file << EOL
+#!/bin/bash
+java com/ReasoningTechnology/Ariadne/$file
+EOL
+chmod +x shell/$file
+
done
-echo "make done"
+echo "$script_name done."
#!/bin/bash
-# Ensure REPO_HOME and ENV_DEV_BUILD are set
+script_name=$(basename ${BASH_SOURCE[0]})
if [ -z "$REPO_HOME" ]; then
- echo "release:: REPO_HOME is not set."
+ echo "$script_name:: REPO_HOME is not set."
exit 1
fi
-if [ -z "$ENV_DEV_BUILD" ]; then
- echo "release:: ENV_DEV_BUILD is not set."
+if [ -z "$ENV_LOCAL_DEVELOPER" ]; then
+ echo "$script_name:: ENV_DEV_BUILD is not set."
exit 1
fi
-# Define release directory
-release_dir="$REPO_HOME/release_candidate"
+release_dir="$REPO_HOME/release"
+shell_dir="$REPO_HOME/developer/shell"
+Ariadne_jar_fp="$REPO_HOME/developer/jvm/Ariadne.jar"
+wrapper=build
+
+
-# Create release directory if it doesn't exist
if [ ! -d "$release_dir" ]; then
mkdir -p "$release_dir"
fi
echo "Starting release process..."
-# Paths to shell wrappers and JAR file
-shell_dir="$REPO_HOME/developer/shell"
-Ariadne_jar_fp="$REPO_HOME/developer/jvm/Ariadne.jar"
-
# Install the JAR file
install_file "$Ariadne_jar_fp" "$release_dir" "ug+r"
# Install shell wrappers
-for wrapper in black blue green; do
+for wrapper in $wrapper; do
install_file "$shell_dir/$wrapper" "$release_dir" "ug+r+x"
done
-echo "Release process completed successfully."
+echo "$script_name done."
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
+
<title>Directory Structure Description</title>
- <style>
- body {
- background-color: hsl(0, 0%, 0%);
- color: hsl(42, 100%, 80%);
- font-family: monospace;
- padding: 20px;
- margin: 0;
- }
- .page {
- padding: 20px;
- margin: 20px auto;
- max-width: 800px;
- background-color: hsl(0, 0%, 10%);
- box-shadow: 0 0 10px hsl(42, 100%, 50%);
- }
- ul, li {
- list-style-type: none;
- }
- li::before {
- content: "📁 ";
- margin-right: 5px;
- }
- li {
- margin-bottom: 5px;
- }
- .description {
- margin-left: 10px;
- color: hsl(42, 100%, 60%);
- }
- h1 {
- text-align: center;
- color: hsl(42, 100%, 80%);
- text-transform: uppercase;
- margin-bottom: 20px;
- }
- h2 {
- color: hsl(42, 100%, 80%);
- text-transform: uppercase;
- margin-top: 40px;
- }
- p {
- color: hsl(42, 100%, 70%);
- margin-bottom: 20px;
- }
- </style>
+<style>
+ html {
+ font-size: 16px; /* This will be the base for rem units */
+ }
+
+ body {
+ font-family: 'Noto Sans JP', Arial, sans-serif;
+ background-color: hsl(0, 0%, 10%);
+ color: hsl(42, 100%, 80%);
+ padding: 2rem;
+ margin: 0;
+ }
+
+ .page {
+ padding: 1.25rem; /* 20px */
+ margin: 1.25rem auto; /* 20px */
+ max-width: 46.875rem; /* 750px */
+ background-color: hsl(0, 0%, 0%);
+ box-shadow: 0 0 0.625rem hsl(42, 100%, 50%); /* 10px */
+ }
+
+ ul, li {
+ font-size: 1rem; /* Keeping default font size */
+ list-style-type: none;
+ }
+
+ li::before {
+ content: "📁 ";
+ margin-right: 0.3125rem; /* 5px */
+ }
+
+ li {
+ margin-bottom: 0.3125rem; /* 5px */
+ }
+
+ .description {
+ margin-left: 0.625rem; /* 10px */
+ color: hsl(42, 100%, 75%);
+ }
+
+ code {
+ font-family: 'Courier New', Courier, monospace;
+ background-color: hsl(0, 0%, 25%);
+ color: hsl(42, 100%, 90%);
+ padding: 0.125rem 0.25rem; /* 2px 4px */
+ border-radius: 0.1875rem; /* 3px */
+ font-size: 90%;
+ }
+
+ h1 {
+ text-align: center;
+ color: hsl(42, 100%, 84%);
+ text-transform: uppercase;
+ margin-bottom: 1.25rem; /* 20px */
+ }
+
+ h2 {
+ color: hsl(42, 100%, 84%);
+ text-transform: uppercase;
+ margin-top: 2.5rem; /* 40px */
+ }
+
+ p {
+ color: hsl(42, 100%, 90%);
+ margin-bottom: 1.25rem; /* 20px */
+ text-align: justify;
+ }
+</style>
+
</head>
<body>
<h2>Reference</h2>
<ul>
- <li>/var/user_data/Thomas-developer/Ariadne</li>
+ <li>Ariadne/<span class="description">aka REPO_HOME, top level owned by the project administrator.</span></li>
<ul>
- <li>developer/ <span class="description">The workspace for developers to organize source code, build scripts, and development-specific tools.</span></li>
+ <li>developer/ <span class="description">Workspace for the developer. Has the source code, build scripts, and development-specific tools.</span></li>
<ul>
- <li>deprecated/ <span class="description">Archived files and older versions no longer in active use.</span></li>
- <li>document/ <span class="description">Developer-level documentation for building and understanding the project.</span></li>
- <li>javac/ <span class="description">Java source files prepared for compilation.</span></li>
- <li>jvm/ <span class="description">Compiled Java bytecode files for the project.</span></li>
- <li>scratch_pad/ <span class="description">Temporary storage for intermediate files during development.</span></li>
- <li>shell/ <span class="description">Shell scripts intended to be part of the project release or build automation.</span></li>
- <li>tool/ <span class="description">Developer-specific tools used for building or managing development tasks.</span></li>
+ <li>deprecated/ <span class="description">Files and older versions being viewed, perhaps part of a refactoring effort.</span></li>
+ <li>document/ <span class="description">Documentation on developing and building the project.</span></li>
+ <li>javac/ <span class="description">Java source files for compilation.</span></li>
+ <li>jvm/ <span class="description">Compiled Java bytecode files for the project, typically a jar for a Java project.</span></li>
+ <li>scratch_pad/ <span class="description">Temporary storage typically for intermediate files created during build.</span></li>
+ <li>shell/ <span class="description">Shell scripts intended to be part of the project release. (These are not tools.)</span></li>
+ <li>tool/ <span class="description">Tools created by the developer, used for development tasks.</span></li>
</ul>
- <li>document/ <span class="description">Top-level project documentation for project managers or contributors.</span></li>
- <li>release_candidate/ <span class="description">Builds and packages ready for testing and release preparation.</span></li>
- <li>scratch_pad/ <span class="description">Temporary storage for project management tasks.</span></li>
- <li>tester/ <span class="description">Files and tools for managing and running test environments.</span></li>
+ <li>document/ <span class="description">General documentation about the project.</span></li>
+ <li>release/ <span class="description">Release candidate for testing. Becomes the release on the release branch.</span></li>
+ <li>scratch_pad/ <span class="description">Temporary storage for project administration tasks.</span></li>
+ <li>tester/ <span class="description">Workspace for the tester. Has the test bench, tests, and test scripts.</span></li>
<ul>
- <li>document/ <span class="description">Test-specific documentation for procedures and setups.</span></li>
+ <li>document/ <span class="description">Test-specific documentation.</span></li>
<li>test0/ <span class="description">Test case 0 environment and associated scripts.</span></li>
<li>test1/ <span class="description">Test case 1 environment and associated scripts.</span></li>
<li>test2/ <span class="description">Test case 2 environment and associated scripts.</span></li>
<li>tool/ <span class="description">Tools needed for testing and managing the test environment.</span></li>
</ul>
- <li>tool/ <span class="description">Project management/administration specific tools.</span></li>
+ <li>tool/ <span class="description">Project administration specific tools.</span></li>
<li>tool_shared/ <span class="description">Tools shared across project roles.</span></li>
<ul>
- <li>bespoke/ <span class="description">Tools developed specifically for this project.</span></li>
+ <li>bespoke/ <span class="description">Shared tools developed within this project.</span></li>
<li>customized/ <span class="description">Modified versions of third-party tools adapted for the project.</span></li>
<li>document/ <span class="description">Documentation related to shared tools and setup.</span></li>
- <li>third_party/ <span class="description">Tools sourced from third-party vendors or open-source projects.</span></li>
+ <li>third_party/ <span class="description">Shared tools sourced from third-party vendors or open-source projects. These have their own independent licenses,</span></li>
</ul>
<li>LICENSE.txt <span class="description">The project license detailing usage and distribution terms.</span></li>
<li>README.md <span class="description">A general overview and introduction to the project.</span></li>
</ul>
</ul>
-
<h2>Name origin and rationale</h2>
- <p>Typically, developers and project administrators do not employ a semantic
- system for naming directories, but more commonly use conventional
- placeholder names. The intended purpose of files in a directory with a
- placeholder name then must be inferred from experience or inspection of
- the files, or learned from other people or from documents.</p>
+ <p>Developers and project administrators typically do not employ a semantic system for
+ naming directories, but more commonly use conventional placeholder
+ names. The intended purpose of files in a directory with a placeholder
+ name then must be inferred from experience or inspection of the files, or
+ learned from documents or other people.</p>
- <p>For example, a directory named 'exe/' probably derives its name from the
+ <p>For example, a directory named <code>exe/</code> probably derives its name from the
fact that the contained files have their executable permission bit set;
however, such a directory will not contain all such files. There might
- even be some files in an `exe/` directory that do not have their
- executable permission bit set. The two concepts being an `exe/` file
- (i.e. being a file in an `exe/` directory) and being an executable file
- are not identical. The actual intended meaning of being an `exe/` file
+ even be some files in an <code>exe/</code> directory that do not have their
+ executable permission bit set. The two concepts being an <code>exe/</code> file
+ (i.e. being a file in an <code>exe/</code> directory) and being an executable file
+ are not identical. The actual intended meaning of being an <code>exe/</code> file
will sometimes be that the contained files are applications available to a
user, or that they are tools available for use in a project.
</p>
property-based file system. In such a system a number of files and
agents are defined. Then we can ask questions about their relationships.
Files with a relationship to the developer are collected, and this
- becomes the `developer/` directory. In a similar manner we get the
- directories, `tester/', and `javac/`. In this latter case the
+ becomes the <code>developer/</code> directory. In a similar manner we get the
+ directories, <code>tester/</code>, and <code>javac/</code>. In this latter case the
agent is a compiler rather than a role.
</p>
- <p>However, when attempting applying this method in practice it became
- apparent that the recognition of relationships was insufficient. Consider
- the directories `deprecated` and `scratch_pad`. There is no
- 'Mr. Deprecated' or 'Mr. Scratch_Pad' who the contained files are
- for. (And this conclusion is not for the lack of trying. Even mythological
- beings did not suffice as agents.) Rather than saying a file has a
- relationship with an agent, these properties (directory names) are states
- imposed by decree by an agent on a file. Perhaps the developer, has
+ <p>When attempting to apply the <code>is-for</code> property in practice it
+ became apparent that using this sole property was insufficient. Consider
+ the directories <code>deprecated/</code> and <code>scratch_pad/</code>. There is no
+ <em>Mr. Deprecated</em> or <em>Mr. Scratch_Pad</em> who the contained
+ files are for. (And this conclusion is not for the lack of trying. Even
+ mythological beings did not suffice as agents.) Rather than being for an
+ agent, the files collected in such a directory have in common a state of
+ being that was imposed upon them by decree. Perhaps the developer, has
decreed that a file is now deprecated, or a build script has decreed that
it is a scratch_pad file. Such decrees are typically more dynamic than the
relationship properties. Also, these properties are disconnected from the
- contents of the file. When, for example, we say a file is for the java
- compiler we can surmise something about its contents. However when we say
- a file is `deprecated` we can surmise nothing about its contents.
+ contents of the file. When, for example, a file has the property of being
+ for the java compiler, we gain some information about its contents. In the
+ universe of possible messages sent through a file, such a file will
+ contain text that is proposed to be java syntax conforming. In contrast,
+ when we learn that a file is <code>deprecated/</code> we gain no
+ information about the contents of the file, because any file can
+ be <code>deprecated</code>, independent of its contents.
</p>
<p>To understand a directory name within this system, one can imagine
reading said name as part of a sentence that integrates the
- property. Consider two property names: 'is-a' and 'is-for'. For example,
- "Each file in the <code>document/</code> directory is a document," or
- "Each file in the <code>developer/</code> directory is for the developer."
- Although the property name is not carried over to the conventional file
- system, we can typically infer what it must be. It is beyond the scope of
- discussion here, but in actuality, our collections are defined by
- predicates that are given a file's properties and relationships as
- arguments, where the predicate resolves to true if and only if the file
- belongs to the collection. Now wouldn't that be interesting if we
- instead derived a probability?
+ property. Consider two property names: <code>is-a</code>
+ and <code>is-for</code>. For example, "Each file in
+ the <code>document/</code> directory <code>is-a</code> document," or "Each
+ file in the <code>developer/</code> directory <code>is-for</code> the
+ developer." Although the property name is not carried over from the
+ property based file system to the conventional file system, we can
+ typically infer what it must have been. (It is beyond the scope of
+ discussion here, but in actuality, property based file system collections
+ are defined by predicates. Each predicate is given a file's properties and
+ relationships as arguments, then resolves to true if and only if the file
+ belongs to the collection. Now wouldn't that be interesting if we instead
+ derived a probability?)
</p>
<p>It is uncommon for a property value to be plural. While it is not
property. Hence when we make a file collection based on a shared property,
then carry that over as a directory name in a conventional file system,
the resulting directory name will often be singular. This pattern can be
- observed in the case of the `document/` directory, as shown in the prior
- paragraph.
+ observed in the case of the <code>document/</code> directory, as shown in
+ the prior paragraph.
</p>
</div>
--- /dev/null
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Black
--- /dev/null
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Blue
--- /dev/null
+#!/bin/bash
+java com/ReasoningTechnology/PaintIt/Green
+++ /dev/null
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Black
+++ /dev/null
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Blue
+++ /dev/null
-#!/bin/bash
-java com/ReasoningTechnology/PaintIt/Green
--- /dev/null
+import java.util.Map;
+
+public class TestBench {
+
+ public static void runTest_Map(Map<String, Boolean> test_map){
+ int totalTest_Map = test_map.size();
+ int passedTest_Map = 0;
+ int failedTest_Map = 0;
+
+ for( Map.Entry<String, Boolean> test : test_map.entrySet() ){
+ try{
+ if( test.getValue() ){
+ passedTest_Map++;
+ } else{
+ System.out.println( "failed: " + test.getKey() );
+ failedTest_Map++;
+ }
+ } catch(Exception e){
+ System.out.println( "failed: " + test.getKey() );
+ failedTest_Map++;
+ }
+ }
+
+ System.out.println("Total test_map run: " + totalTest_Map);
+ System.out.println("Total test_map passed: " + passedTest_Map);
+ System.out.println("Total test_map failed: " + failedTest_Map);
+ }
+
+}
--- /dev/null
+#!/usr/bin/env bash
+
+## input guards
+
+# instead of bailing, we will go fetch what we need
+env_should_be="tool_shared/bespoke/env"
+if [ "$ENV" != "$env_should_be" ]; then
+ source "$REPO_HOME"/"$env_should_be"
+fi
+
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "$script_name:: This script must be sourced, not executed."
+ exit 1
+fi
+
+cd "$REPO_HOME"/tester/
+
+export ENV=$(script_fp)
+export PROJECT="$PROJECT"_tester
+
+export PATH=\
+"$REPO_HOME"/tester/tool/\
+:"$REPO_HOME"/tool_shared/bespoke/\
+"$JAVA_HOME"/bin\
+:"$PATH"
+
+export CLASSPATH=\
+:"$REPO_HOME"/developer/release/Ariadne.jar\
+:"$JAVA_HOME"/lib\
+:"$CLASSPATH"
+
+echo "$ENV complete."
+++ /dev/null
-#!/usr/bin/env bash
-# environment common to all tests
-# each test directory also has an environment
-
-export JAVA_HOME="$REPO_HOME/tool/jdk-11"
-export GROOVY_HOME="$REPO_HOME/tool/groovy-4.0.9"
-
-export PATH=\
-"$REPO_HOME"/release_candidate\
-:"$JAVA_HOME"/bin\
-:"$GROOVY_HOME"/bin\
-:"$PATH"
--- /dev/null
+#!/bin/env bash
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$ENV_TESTER_LOCAL" ]; then
+ echo "make.sh:: script can only be run in the tester environment"
+ env_error=true
+fi
+
+
+
+
+echo "$script_name done."
+++ /dev/null
-#!/bin/env bash
-
-if [ -z "$ENV_TESTER" ]; then
- echo "make.sh:: script can only be run in the tester environment"
- env_error=true
-fi
-
-set -x
-
-groovyc TestGraph.groovy
--- /dev/null
+#!/usr/bin/env bash
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "$script_name must be sourced, not executed. Exiting."
+ exit 1
+fi
+
+if [ -z "$ENV_BASE" ]; then
+ script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+ source "${script_path}/env_base"
+fi
+
+ENV_PM=true
+
+PROJECT="$PROJECT"_administrator
+
+export PATH=\
+"$REPO_HOME"/tool\
+:"$PATH"
+
+# no sneaky hidden files
+alias ls="ls -a"
+
+export ENV_PM=true
+echo "$script_name done."
+
+++ /dev/null
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "This script must be sourced, not executed. Exiting."
- return 1
-fi
-
-if [ -z "$ENV_BASE" ]; then
- script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
- source "${script_path}/env_base"
-fi
-
-ENV_PM=true
-
-PROJECT="$PROJECT"_administrator
-
-export PATH=\
-"$REPO_HOME"/tool\
-:"$PATH"
-
-# no sneaky hidden files
-alias ls="ls -a"
-
-
-export ENV_PM=true
-echo "${BASH_SOURCE[0]}" "complete"
+++ /dev/null
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "This script must be sourced, not executed. Exiting."
- return 1
-fi
-
-# These are things set by the `repo` command found in the `resource` project,
-# but if you don't have that, then source this into the environment.
-
-script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
-export REPO_HOME="${script_path%/*}/"
-export PROJECT=$(basename "$REPO_HOME")
-
-PPS1="\n[$PROJECT]\n\u@\h§$(pwd)§\n> "
-PPS2=">> "
-
-echo REPO_HOME "$REPO_HOME"
-echo PROJECT "$PROJECT"
-echo "${BASH_SOURCE[0]}" "complete"
-
-export ENV_BASE=true
+++ /dev/null
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "This script must be sourced, not executed. Exiting."
- return 1
-fi
-
-if [ -z "$ENV_BASE" ]; then
- script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
- source "${script_path}/env_base"
-fi
-
-PROJECT="$PROJECT"_developer
-
-export PATH=\
-"$REPO_HOME"/developer/tool/\
-:"$REPO_HOME"/tool_shared/bespoke/\
-:"$PATH"
-
-# so the .gitignore files can be seen:
-alias ls="ls -a"
-
-cd "$REPO_HOME"/developer/
-export ENV_DEVELOPER=true
-source tool/env
-
-echo "${BASH_SOURCE[0]}" "complete"
-
-
-
+++ /dev/null
-#!/usr/bin/env bash
-
-# Ensure the script is sourced
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- echo "This script must be sourced, not executed. Exiting."
- return 1
-fi
-
-# Check if REPO_HOME is set, if not source env_base
-if [ -z "$ENV_BASE" ]; then
- script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
- source "${script_path}/env_base"
-fi
-
-PROJECT="$PROJECT"_TESTER
-
-export PATH=\
-"$REPO_HOME"/tester/executor/\
-:"$REPO_HOME"/tool_shared/bespoke/\
-:"$PATH"
-
-cd "$REPO_HOME"/tester/
-export ENV_TESTER=true
-source tool/env
-
-echo "${BASH_SOURCE[0]}" "complete"
--- /dev/null
+#!/bin/bash
+
+# cp subtree at <source> under file path <destination>, and make all the copied
+# files read-only. The intended use case is for moving files to a `deprecated`
+# directory. This helps prevent subsequent accidental editing.
+
+SCRIPT_NAME=$(basename "$0")
+if [ "$#" -lt 2 ]; then
+ echo "Usage: $SCRIPT_NAME <source> <destination>"
+ exit 1
+fi
+SRC="$1"
+DEST="$2"
+
+mkdir -p "$DEST"
+mv "$SRC" "$DEST"
+
+# make stuff readonly
+cd "$DEST" || exit
+chmod -R u-w,go-rwx "$DEST"
--- /dev/null
+#!/usr/bin/env bash
+# environment shared by entire project
+
+# Path to this script
+script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+
+
+
+# Path to the repo is two directories up
+export REPO_HOME="${script_path%/*/*}/"
+
+# script name and path relative to the top of the project
+script_fn(){
+ echo "$(basename ${BASH_SOURCE[0]})"
+}
+script_fp(){
+ echo "$(realpath --relative-to="$REPO_HOME" "$script_path")/$(script_fn)"
+}
+export -f script_fn script_fp
+
+# REPO_HOME relative file path to this script
+# Calling the function correctly without braces
+export ENV=$(script_fp)
+
+# this script must be sourced, sort of like Python activate
+if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
+ echo "$ENV must be sourced, not executed. Exiting."
+ exit 1
+fi
+
+export PROJECT=$(basename "$REPO_HOME")
+
+# Third party tool(s) that the project admin kindly installed.
+# The developer and tester should use these links to find them.
+export JAVA_HOME="$REPO_HOME/tool_shared/third_party/jdk-11"
+
+# set the shell prompt to show the project
+PPS1="\n[$PROJECT]\n\u@\h§$(pwd)§\n> "
+PPS2=">> "
+
+echo ENV "$ENV"
+echo REPO_HOME "$REPO_HOME"
+echo PROJECT "$PROJECT"
+echo "$ENV completed."
+
+
--- /dev/null
+#!/usr/bin/env bash
+
+# Path to this script
+script_path="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
+
+# Path to the repo is two directories up
+export REPO_HOME="${script_path%/*/*}/"
+
+# script name and path relative to the top of the project
+script_fn(){
+ echo "$(basename ${BASH_SOURCE[0]})"
+}
+script_fp(){
+ echo "$(realpath --relative-to="$REPO_HOME" "$script_path")/$(script_fn)"
+}
+
+# REPO_HOME relative file path to this script
+# Calling the function correctly without braces
+export ENV=$(script_fp)
+
+# Output the values
+echo "Script path: $script_path"
+echo "Repo home: $REPO_HOME"
+echo "ENV: $ENV"
#!/bin/env bash
-echo 0.1
+# 0.1 pulled groovy files from GQL_to_Cypher build.gradle
+# 0.2 conversion to Java
+# 0.3 refactored, split into smaller pieces, made into package
+
+echo 0.3
--- /dev/null
+#!/bin/bash
+# remove all files in the release directory
+set -e
+
+script_name=$(basename ${BASH_SOURCE[0]})
+if [ -z "$REPO_HOME" ]; then
+ echo "$script_name:: REPO_HOME is not set."
+ exit 1
+fi
+
+set -x
+cd "$REPO_HOME"
+rm -rf release/*
+set +x
+
+echo "$script_name done."
+