From: Thomas Walker Lynch Date: Mon, 9 Dec 2024 11:11:55 +0000 (+0000) Subject: pencil updates X-Git-Url: https://git.reasoningtechnology.com/style/static/git-logo.png?a=commitdiff_plain;h=cec5f559113c29384698af578d0ef692b4192212;p=Ariadne pencil updates --- diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 120000 index 98f0753..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -document/license.txt \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..e177f6f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,152 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that +entity. For the purposes of this definition, "control" means (i) the power, +direct or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of +the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and You must cause any modified files to carry prominent notices +stating that You changed the files; and You must retain, in the Source form of +any Derivative Works that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, excluding those notices +that do not pertain to any part of the Derivative Works; and If the Work +includes a "NOTICE" text file as part of its distribution, then any Derivative +Works that You distribute must include a readable copy of the attribution +notices contained within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one of the following +places: within a NOTICE text file distributed as part of the Derivative Works; +within the Source form or documentation, if provided along with the Derivative +Works; or, within a display generated by the Derivative Works, if and wherever +such third-party notices normally appear. The contents of the NOTICE file are +for informational purposes only and do not modify the License. You may add Your +own attribution notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided that such +additional attribution notices cannot be construed as modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/README.txt b/README.txt deleted file mode 120000 index 06fa5f4..0000000 --- a/README.txt +++ /dev/null @@ -1 +0,0 @@ -document/readme.txt \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..bf5978e --- /dev/null +++ b/README.txt @@ -0,0 +1,80 @@ + +-------------------------------------------------------------------------------- +Ariadne + +Ariadne is a graph based build tool. + +Tying to build a project is a lot like trying to find your way out of +Daedalus's impossible to solve maze. Ariadne hands you a ball of string. + +-------------------------------------------------------------------------------- +Documents + +Note the directories: Ariadne/document, Ariadne/developer/document, +and Ariadne/tester/document. + + +-------------------------------------------------------------------------------- +How It Works + +Everything is coded in Java, including the developers description of the +dependency graph. + +A graph is made of nodes and edges. + +A node is dictionary. Each node has a 'label', a 'build' function, and a +'neighbor' list. The neighbor list holds the edges. + +Using Java, the developer puts the nodes in a map, keyed on the node label, or +writes functions that when given a label, return either a node or null. + +A node map looks a lot like classic make file. Each node label is a target file +path, the neighbor list can be listed next, and then we have the build code. + +Function defined nodes are very flexible. They are more powerful, and more +flexible than make's pattern rules. If a developer wants a function to act like +a pattern rule, the developer can use a regular expression match on the given +label. However, unlike make, pattern rules defined in this manner, will +backwards chain potentially through other pattern rules. + +The Build function is one of the graph traversal programs. It is given a graph +and a list of top level market targets. It traverses the graph to find leaf +while unwinding its proverbial ball of string. Upon finding leaf nodes, it then +follows the string back until building the targets. + +It is possible to define other graph traversal functions. In fact, Ariadne can +be used to do anything where a directed graph of functions is useful. + + +-------------------------------------------------------------------------------- +Entering the Project + +The project has three entry points, one for each project role: developer, +tester, and administrator. To enter the project, source the environment +appropriate for the role, either `env_developer`, `env_tester`, or +`env_administrator`. + +For example, to work on the code as a developer, start a fresh shell and: + +> cd Ariadne +> . env_developer +> + +[do work] + +> make +> release + +[change to tester role] + +The `make` command you see above is a bash script. Version 1.0 of Ariadne uses a +direct 'build it all every time' approach. Perhaps in version 2.0 or so, we will +use a prior version of Ariadne for the build environment. + + +Using Ariadne +------------- + +After it is built and released the tool will appear in the Ariadne/release +directory where it can be tested or imported into another project. + diff --git a/developer/bash/.githolder b/developer/bash/.githolder new file mode 100644 index 0000000..e69de29 diff --git a/developer/bash/Build b/developer/bash/Build new file mode 100755 index 0000000..2a18026 --- /dev/null +++ b/developer/bash/Build @@ -0,0 +1,2 @@ +#!/bin/bash +java com.ReasoningTechnology."Ariadne".Build diff --git a/developer/deprecated/Ariandne.groovy b/developer/deprecated/Ariandne.groovy deleted file mode 100644 index a657f0d..0000000 --- a/developer/deprecated/Ariandne.groovy +++ /dev/null @@ -1,457 +0,0 @@ -/* - 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) // Renamed to 'file' to avoid overwriting 'file_fn' - - // Get the parent directory path - def parent_dp = file.getParent() - - // Get the file name (with extension) - def file_fn = file.getName() - - // Split the file name into base and extension - def file_fn_base = file_fn.lastIndexOf('.') > 0 ? file_fn[0..file_fn.lastIndexOf('.') - 1] : file_fn - 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 diff --git a/developer/deprecated/Ariandne2.groovy b/developer/deprecated/Ariandne2.groovy deleted file mode 100644 index 6c0cf17..0000000 --- a/developer/deprecated/Ariandne2.groovy +++ /dev/null @@ -1,635 +0,0 @@ -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 diff --git a/developer/deprecated/Graph.java b/developer/deprecated/Graph.java deleted file mode 100644 index 3052338..0000000 --- a/developer/deprecated/Graph.java +++ /dev/null @@ -1,685 +0,0 @@ -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{} - public class LabelList extends ArrayList