1 //usr/bin/env jshell  "$0" "$@"; exit $?
  2 //usr/bin/env jshell --execution local "$0" "$@"; exit $?
  3 
  4 /*
  5  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  7  *
  8  * This code is free software; you can redistribute it and/or modify it
  9  * under the terms of the GNU General Public License version 2 only, as
 10  * published by the Free Software Foundation.  Oracle designates this
 11  * particular file as subject to the "Classpath" exception as provided
 12  * by Oracle in the LICENSE file that accompanied this code.
 13  *
 14  * This code is distributed in the hope that it will be useful, but WITHOUT
 15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 17  * version 2 for more details (a copy is included in the LICENSE file that
 18  * accompanied this code).
 19  *
 20  * You should have received a copy of the GNU General Public License version
 21  * 2 along with this work; if not, write to the Free Software Foundation,
 22  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 23  *
 24  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 25  * or visit www.oracle.com if you need additional information or have any
 26  * questions.
 27  */
 28 
 29 import javax.xml.transform.OutputKeys;
 30 import javax.xml.transform.TransformerFactory;
 31 import javax.xml.transform.dom.DOMSource;
 32 import javax.xml.transform.stream.StreamResult;
 33 import java.io.*;
 34 import java.util.*;
 35 import java.util.regex.*;
 36 import java.nio.file.*;
 37 import java.util.stream.Stream;
 38 import java.nio.charset.Charset;
 39 import java.nio.charset.StandardCharsets;
 40 public class PomChecker {
 41    public static class XMLNode {
 42       org.w3c.dom.Element element;
 43       List<XMLNode> children = new ArrayList<>();
 44       Map<String, String> attrMap =  new HashMap<>();
 45 
 46       XMLNode(org.w3c.dom.Element element) {
 47          this.element = element;
 48          this.element.normalize();
 49          for (int i = 0; i < this.element.getChildNodes().getLength(); i++) {
 50             if (this.element.getChildNodes().item(i) instanceof org.w3c.dom.Element e){
 51                this.children.add(new XMLNode(e));
 52             }
 53          }
 54          for (int i = 0; i < element.getAttributes().getLength(); i++) {
 55             if (element.getAttributes().item(i) instanceof org.w3c.dom.Attr attr){
 56                this.attrMap.put(attr.getName(),attr.getValue());
 57             }
 58          }
 59       }
 60       public boolean hasAttr(String name) { return attrMap.containsKey(name); }
 61       public String attr(String name) { return attrMap.get(name); }
 62       XMLNode(File file) throws Throwable {
 63          this(javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file).getDocumentElement());
 64       }
 65       void write(File file) throws Throwable {
 66          var  transformer = TransformerFactory.newInstance().newTransformer();
 67          transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 68          transformer.setOutputProperty(OutputKeys.METHOD, "xml");
 69          transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
 70          transformer.transform(new DOMSource(element.getOwnerDocument()), new StreamResult(file));
 71       }
 72    }
 73    static Pattern varPattern=Pattern.compile("\\$\\{([^}]*)\\}");
 74    static Pattern trailingWhitespacePattern=Pattern.compile(".*  *");
 75    static public String varExpand(Map<String,String> props, String value){ // recurse
 76       String result = value;
 77       if (varPattern.matcher(value) instanceof Matcher matcher && matcher.find()) {
 78          var v = matcher.group(1);
 79          result = varExpand(props,value.substring(0, matcher.start())
 80                +(v.startsWith("env")
 81                   ?System.getenv(v.substring(4))
 82                   :props.get(v))
 83                +value.substring(matcher.end()));
 84          //out.println("incomming ='"+value+"'  v= '"+v+"' value='"+value+"'->'"+result+"'");
 85       }
 86       return result;
 87    }
 88    static boolean isParent(File possibleParent, File maybeChild){
 89       File parent = maybeChild.getParentFile();
 90       while ( parent != null ) {
 91          if ( parent.equals( possibleParent ) )
 92             return true;
 93          parent = parent.getParentFile();
 94       }
 95       return false;
 96    }
 97 
 98 
 99 
100    public static void main(String[] args) throws Throwable{
101       var out = System.out;
102       var err = System.out;
103 
104       var osArch = System.getProperty("os.arch");
105       var osName = System.getProperty("os.name");
106       var osVersion = System.getProperty("os.version");
107       var javaVersion = System.getProperty("java.version");
108       var javaHome = System.getProperty("java.home");
109       var pwd = new File(System.getProperty("user.dir"));
110 
111       if (javaVersion.startsWith("24")){
112          //out.println("javaVersion "+javaVersion+" looks OK");
113 
114          var props = new LinkedHashMap<String,String>();
115          var dir = new File(".");
116          var topPom = new XMLNode(new File(dir,"pom.xml"));
117          var babylonDirKey = "babylon.dir";
118          var spirvDirKey = "beehive.spirv.toolkit.dir";
119          var hatDirKey = "hat.dir";
120          var interestingKeys = Set.of(spirvDirKey, babylonDirKey,hatDirKey);
121          var requiredDirKeys = Set.of(babylonDirKey, hatDirKey);
122          var dirKeyToDirMap = new HashMap<String,File>();
123 
124          topPom.children.stream().filter(e->e.element.getNodeName().equals("properties")).forEach(properties ->
125                properties.children.stream().forEach(property ->{
126                   var key = property.element.getNodeName();
127                   var value = varExpand(props,property.element.getTextContent());
128                   props.put(key, value);
129                   if (interestingKeys.contains(key)){
130                       var file = new File(value);
131                       if (requiredDirKeys.contains(key) && !file.exists()){
132                          err.println("ERR pom.xml has property '"+key+"' with value '"+value+"' but that dir does not exists!");
133                          System.exit(1);
134                       }
135                       dirKeyToDirMap.put(key,file);
136                   }
137                   })
138                );
139          for (var key:requiredDirKeys){
140              if (!props.containsKey(key)){
141                  err.println("ERR pom.xml expected to have property '"+key+"' ");
142                  System.exit(1);
143              }
144          }
145 
146          var javaHomeDir = new File(javaHome);
147          var babylonDir = dirKeyToDirMap.get(babylonDirKey);
148          if (isParent(babylonDir, javaHomeDir)){
149             var hatDir = dirKeyToDirMap.get(hatDirKey);
150             if (hatDir.equals(pwd)){
151                var backendsPom = new XMLNode(new File(dir,"backends/pom.xml"));
152                var modules = backendsPom.children.stream().filter(e->e.element.getNodeName().equals("modules")).findFirst().get();
153                var spirvModule = modules.children.stream().filter(e->e.element.getTextContent().equals("spirv")).findFirst();
154                if (spirvModule.isPresent()){
155                   if (dirKeyToDirMap.containsKey(spirvDirKey)) {
156                      var spirvDir = dirKeyToDirMap.get(spirvDirKey);
157                      if (!spirvDir.exists()) {
158                         err.println("ERR "+spirvDirKey + " -> '" + spirvDir + "' dir does not exists but module included in backends ");
159                         System.exit(1);
160                      }
161                   }else{
162                      err.println("ERR "+spirvDirKey + " -> variable dir does not exists but module included in backends ");
163                      System.exit(1);
164                   }
165                } else{
166                   if (dirKeyToDirMap.containsKey(spirvDirKey)) {
167                      var spirvDir = dirKeyToDirMap.get(spirvDirKey);
168                      if (spirvDir.exists()){
169                         out.println("WRN "+spirvDirKey+" -> '"+spirvDir+"' exists but spirv module not included in backends ");
170                      }else{
171                         out.println("INF "+spirvDirKey+" -> '"+spirvDir+"' does not exist and not included in backends ");
172                      }
173                   }
174                }
175             } else{
176                err.println("ERR hat.dir='"+hatDir+"' != ${pwd}='"+pwd+"'");
177                System.exit(1);
178             }
179          }else{
180             err.println("ERR babylon.dir '"+babylonDir+"' is not a child of javaHome '"+javaHome+"'");
181             System.exit(1);
182          }
183       }else{
184          err.println("ERR Incorrect Java version. Is babylon jdk in your path?");
185          System.exit(1);
186       }
187 
188       Set.of("hat", "examples", "backends").forEach(dirName->{
189          try{
190             Files.walk(Paths.get(dirName)).filter(p->{
191               var name = p.toString();
192               return !name.contains("cmake-build-debug")
193                 && !name.contains("rleparser")
194                 && ( name.endsWith(".java") || name.endsWith(".cpp") || name.endsWith(".h"));
195               }).forEach(path->{
196                 try{
197                    boolean license = false;
198                    for (String line: Files.readAllLines(path,  StandardCharsets.UTF_8)){
199                       if (line.contains("\t")){
200                         err.println("ERR TAB "+path+":"+line);
201                       }
202                       if (line.endsWith(" ")) {
203                         err.println("ERR TRAILING WHITESPACE "+path+":"+line);
204                       }
205                       if (Pattern.matches("^  *(package|import).*$",line)) { // I saw this a few times....?
206                         err.println("ERR WEIRD INDENT "+path+":"+line);
207                       }
208                       if (Pattern.matches("^.*Copyright.*202[4-9].*Oracle.*$",line)) { // not foolproof I know
209                         license = true;
210                       }
211                    }
212                    if (!license){
213                       err.println("ERR MISSING LICENSE "+path);
214                    }
215                 } catch(IOException ioe){
216                   err.println(ioe);
217                 }
218             });
219          } catch(IOException ioe){
220            err.println(ioe);
221          }
222       });
223    }
224 }
225