1 19 20 package org.netbeans.nbbuild; 21 22 import java.io.File ; 23 import java.io.IOException ; 24 import java.util.ArrayList ; 25 import java.util.Arrays ; 26 import java.util.HashMap ; 27 import java.util.HashSet ; 28 import java.util.Hashtable ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.Map ; 32 import java.util.Set ; 33 import java.util.StringTokenizer ; 34 import java.util.jar.Attributes ; 35 import java.util.jar.JarFile ; 36 import org.apache.tools.ant.BuildListener; 37 import org.apache.tools.ant.Project; 38 import org.apache.tools.ant.taskdefs.Property; 39 import org.apache.tools.ant.types.Path; 40 import org.apache.tools.ant.util.FileUtils; 41 import org.w3c.dom.Document ; 42 import org.w3c.dom.Element ; 43 import org.xml.sax.InputSource ; 44 45 50 final class ModuleListParser { 51 52 53 private static final int DEPTH_NB_ALL = 3; 54 55 private static Map <File ,Map <String ,Entry>> SOURCE_SCAN_CACHE = new HashMap <File ,Map <String ,Entry>>(); 56 private static Map <File ,Map <String ,Entry>> SUITE_SCAN_CACHE = new HashMap <File ,Map <String ,Entry>>(); 57 private static Map <File ,Entry> STANDALONE_SCAN_CACHE = new HashMap <File ,Entry>(); 58 private static Map <File ,Map <String ,Entry>> BINARY_SCAN_CACHE = new HashMap <File ,Map <String ,Entry>>(); 59 60 61 public static void resetCaches() { 62 SOURCE_SCAN_CACHE.clear(); 63 SUITE_SCAN_CACHE.clear(); 64 STANDALONE_SCAN_CACHE.clear(); 65 BINARY_SCAN_CACHE.clear(); 66 } 67 68 71 private static Map <String ,Entry> scanNetBeansOrgSources(File root, Hashtable <String ,String > properties, Project project) throws IOException { 72 Map <String ,Entry> entries = SOURCE_SCAN_CACHE.get(root); 73 if (entries == null) { 74 entries = new HashMap <String ,Entry>(); 75 Set <String > standardModules = new HashSet <String >(); 77 boolean doFastScan = false; 78 String basedir = properties.get("basedir"); 79 if (basedir != null) { 80 File basedirF = new File (basedir); 81 String clusterList = properties.get("nb.clusters.list"); 82 if (clusterList != null) { 83 StringTokenizer tok = new StringTokenizer (clusterList, ", "); 84 while (tok.hasMoreTokens()) { 85 String clusterName = tok.nextToken(); 86 String moduleList = properties.get(clusterName); 87 if (moduleList != null) { 88 StringTokenizer tok2 = new StringTokenizer (moduleList, ", "); 89 while (tok2.hasMoreTokens()) { 90 String module = tok2.nextToken(); 91 standardModules.add(module); 92 doFastScan |= new File (root, module.replace('/', File.separatorChar)).equals(basedirF); 93 } 94 } 95 } 96 } 97 } 98 if (doFastScan) { 99 if (project != null) { 100 project.log("Scanning for modules in " + root + " among standard clusters"); 101 } 102 Iterator it = standardModules.iterator(); 103 while (it.hasNext()) { 104 String module = (String ) it.next(); 105 scanPossibleProject(new File (root, module.replace('/', File.separatorChar)), entries, properties, module, ParseProjectXml.TYPE_NB_ORG, project); 106 } 107 } else { 108 if (project != null) { 110 project.log("Scanning for modules in " + root); 111 project.log("Quick scan mode disabled since " + basedir + " not among standard modules of " + root + " which are " + standardModules, project.MSG_VERBOSE); 112 } 113 doScanNetBeansOrgSources(entries, root, DEPTH_NB_ALL, properties, null, project); 114 } 115 if (project != null) { 116 project.log("Found modules: " + entries.keySet(), Project.MSG_VERBOSE); 117 } 118 SOURCE_SCAN_CACHE.put(root, entries); 119 } 120 return entries; 121 } 122 123 124 private static final String [] EXCLUDED_DIR_NAMES = { 125 "CVS", "nbproject", "www", "test", "build", "src", "org", }; 133 136 private static void doScanNetBeansOrgSources(Map <String ,Entry> entries, File dir, int depth, Hashtable <String ,String > properties, String pathPrefix, Project project) throws IOException { 137 if (depth == 0) { 138 return; 139 } 140 File [] kids = dir.listFiles(); 141 if (kids == null) { 142 return; 143 } 144 KIDS: for (File kid : kids) { 145 if (!kid.isDirectory()) { 146 continue; 147 } 148 String name = kid.getName(); 149 for (String n : EXCLUDED_DIR_NAMES) { 150 if (name.equals(n)) { 151 continue KIDS; 152 } 153 } 154 String newPathPrefix = (pathPrefix != null) ? pathPrefix + "/" + name : name; 155 scanPossibleProject(kid, entries, properties, newPathPrefix, ParseProjectXml.TYPE_NB_ORG, project); 156 doScanNetBeansOrgSources(entries, kid, depth - 1, properties, newPathPrefix, project); 157 } 158 } 159 160 163 private static boolean scanPossibleProject(File dir, Map <String ,Entry> entries, Hashtable <String ,String > properties, String path, int moduleType, Project project) throws IOException { 164 File nbproject = new File (dir, "nbproject"); 165 File projectxml = new File (nbproject, "project.xml"); 166 if (!projectxml.isFile()) { 167 return false; 168 } 169 Document doc; 170 try { 171 doc = XMLUtil.parse(new InputSource (projectxml.toURI().toString()), 172 false, true, null, null); 173 } catch (Exception e) { throw (IOException ) new IOException ("Error parsing project file\n" + projectxml + ": " + e.getMessage()).initCause(e); 176 } 177 Element typeEl = XMLUtil.findElement(doc.getDocumentElement(), "type", ParseProjectXml.PROJECT_NS); 178 if (!XMLUtil.findText(typeEl).equals("org.netbeans.modules.apisupport.project")) { 179 return false; 180 } 181 Element configEl = XMLUtil.findElement(doc.getDocumentElement(), "configuration", ParseProjectXml.PROJECT_NS); 182 Element dataEl = ParseProjectXml.findNBMElement(configEl, "data"); 183 if (dataEl == null) { 184 if (project != null) { 185 project.log(projectxml.toString() + ": warning: module claims to be a NBM project but is missing <data xmlns=\"" + ParseProjectXml.NBM_NS3 + "\">; maybe an old NB 4.[01] project?", Project.MSG_WARN); 186 } 187 return false; 188 } 189 Element cnbEl = ParseProjectXml.findNBMElement(dataEl, "code-name-base"); 190 String cnb = XMLUtil.findText(cnbEl); 191 Project fakeproj = new Project(); 193 if (project != null) { 194 Iterator it = project.getBuildListeners().iterator(); 196 while (it.hasNext()) { 197 fakeproj.addBuildListener((BuildListener) it.next()); 198 } 199 } 200 fakeproj.setBaseDir(dir); Property faketask = new Property(); 202 faketask.setProject(fakeproj); 203 switch (moduleType) { 204 case ParseProjectXml.TYPE_NB_ORG: 205 break; 207 case ParseProjectXml.TYPE_SUITE: 208 faketask.setFile(new File (nbproject, "private/suite-private.properties")); 209 faketask.execute(); 210 faketask.setFile(new File (nbproject, "suite.properties")); 211 faketask.execute(); 212 faketask.setFile(new File (fakeproj.replaceProperties("${suite.dir}/nbproject/private/platform-private.properties"))); 213 faketask.execute(); 214 faketask.setFile(new File (fakeproj.replaceProperties("${suite.dir}/nbproject/platform.properties"))); 215 faketask.execute(); 216 break; 217 case ParseProjectXml.TYPE_STANDALONE: 218 faketask.setFile(new File (nbproject, "private/platform-private.properties")); 219 faketask.execute(); 220 faketask.setFile(new File (nbproject, "platform.properties")); 221 faketask.execute(); 222 break; 223 default: 224 assert false : moduleType; 225 } 226 faketask.setFile(new File (nbproject, "private/private.properties".replace('/', File.separatorChar))); 227 faketask.execute(); 228 faketask.setFile(new File (nbproject, "project.properties")); 229 faketask.execute(); 230 faketask.setFile(null); 231 faketask.setName("module.jar.dir"); 232 faketask.setValue("modules"); 233 faketask.execute(); 234 assert fakeproj.getProperty("module.jar.dir") != null : fakeproj.getProperties(); 235 faketask.setName("module.jar.basename"); 236 faketask.setValue(cnb.replace('.', '-') + ".jar"); 237 faketask.execute(); 238 faketask.setName("module.jar"); 239 faketask.setValue(fakeproj.replaceProperties("${module.jar.dir}/${module.jar.basename}")); 240 faketask.execute(); 241 switch (moduleType) { 242 case ParseProjectXml.TYPE_NB_ORG: 243 assert path != null; 244 for (Map.Entry <String ,String > entry : properties.entrySet()) { 246 String val = entry.getValue(); 247 String [] modules = val.split(", *"); 248 if (Arrays.asList(modules).contains(path)) { 249 String key = entry.getKey(); 250 String clusterDir = properties.get(key + ".dir"); 251 if (clusterDir != null) { 252 faketask.setName("cluster.dir"); 253 faketask.setValue(clusterDir); 254 faketask.execute(); 255 break; 256 } 257 } 258 } 259 faketask.setName("cluster.dir"); 260 faketask.setValue("extra"); faketask.execute(); 262 faketask.setName("netbeans.dest.dir"); 263 faketask.setValue(properties.get("netbeans.dest.dir")); 264 faketask.execute(); 265 faketask.setName("cluster"); 266 faketask.setValue(fakeproj.replaceProperties("${netbeans.dest.dir}/${cluster.dir}")); 267 faketask.execute(); 268 break; 269 case ParseProjectXml.TYPE_SUITE: 270 assert path == null; 271 faketask.setName("suite.dir"); 272 faketask.setValue(properties.get("suite.dir")); 273 faketask.execute(); 274 faketask.setName("cluster"); 275 faketask.setValue(fakeproj.replaceProperties("${suite.dir}/build/cluster")); 276 faketask.execute(); 277 break; 278 case ParseProjectXml.TYPE_STANDALONE: 279 assert path == null; 280 faketask.setName("cluster"); 281 faketask.setValue(fakeproj.replaceProperties("${basedir}/build/cluster")); 282 faketask.execute(); 283 break; 284 default: 285 assert false : moduleType; 286 } 287 File jar = fakeproj.resolveFile(fakeproj.replaceProperties("${cluster}/${module.jar}")); 288 List <File > exts = new ArrayList <File >(); 289 for (Element ext : XMLUtil.findSubElements(dataEl)) { 290 if (!ext.getLocalName().equals("class-path-extension")) { 291 continue; 292 } 293 Element binaryOrigin = ParseProjectXml.findNBMElement(ext, "binary-origin"); 294 File origBin = null; 295 if (binaryOrigin != null) { 296 String reltext = XMLUtil.findText(binaryOrigin); 297 String nball = properties.get("nb_all"); 298 if (nball != null) { 299 faketask.setName("nb_all"); 300 faketask.setValue(nball); 301 faketask.execute(); 302 } 303 fakeproj.setBaseDir(dir); 304 origBin = fakeproj.resolveFile(fakeproj.replaceProperties(reltext)); 305 } 306 307 File resultBin = null; 308 if (origBin == null || !origBin.exists()) { 309 Element runtimeRelativePath = ParseProjectXml.findNBMElement(ext, "runtime-relative-path"); 310 if (runtimeRelativePath == null) { 311 throw new IOException ("Have malformed <class-path-extension> in " + projectxml); 312 } 313 String reltext = XMLUtil.findText(runtimeRelativePath); 314 resultBin = new File (jar.getParentFile(), reltext.replace('/', File.separatorChar)); 316 } 317 318 if (origBin != null) { 319 exts.add(origBin); 320 } 321 322 if (resultBin != null) { 323 exts.add(resultBin); 324 } 325 } 326 List <String > prereqs = new ArrayList <String >(); 327 List <String > rundeps = new ArrayList <String >(); 328 Element depsEl = ParseProjectXml.findNBMElement(dataEl, "module-dependencies"); 329 if (depsEl == null) { 330 throw new IOException ("Malformed project file " + projectxml); 331 } 332 for (Element dep : XMLUtil.findSubElements(depsEl)) { 333 Element cnbEl2 = ParseProjectXml.findNBMElement(dep, "code-name-base"); 334 if (cnbEl2 == null) { 335 throw new IOException ("Malformed project file " + projectxml); 336 } 337 String cnb2 = XMLUtil.findText(cnbEl2); 338 rundeps.add(cnb2); 339 if (ParseProjectXml.findNBMElement(dep, "build-prerequisite") == null) { 340 continue; 341 } 342 prereqs.add(cnb2); 343 } 344 String cluster = fakeproj.getProperty("cluster.dir"); Entry entry = new Entry(cnb, jar, exts.toArray(new File [exts.size()]), dir, path, 346 prereqs.toArray(new String [prereqs.size()]), cluster, rundeps.toArray(new String [rundeps.size()])); 347 if (entries.containsKey(cnb)) { 348 throw new IOException ("Duplicated module " + cnb + ": found in " + entries.get(cnb) + " and " + entry); 349 } else { 350 entries.put(cnb, entry); 351 } 352 return true; 353 } 354 355 358 private static Map <String ,Entry> scanBinaries(Hashtable <String ,String > properties, Project project) throws IOException { 359 String buildS = properties.get("netbeans.dest.dir"); 360 File basedir = new File (properties.get("basedir")); 361 if (buildS == null) { 362 throw new IOException ("No definition of netbeans.dest.dir in " + basedir); 363 } 364 File build = FileUtils.getFileUtils().normalize(FileUtils.getFileUtils().resolveFile(basedir, buildS).getAbsolutePath()); 367 if (!build.isDirectory()) { 368 throw new IOException ("No such netbeans.dest.dir: " + build); 369 } 370 Map <String ,Entry> entries = BINARY_SCAN_CACHE.get(build); 371 if (entries == null) { 372 if (project != null) { 373 project.log("Scanning for modules in " + build); 374 } 375 entries = new HashMap <String ,Entry>(); 376 doScanBinaries(build, entries); 377 if (project != null) { 378 project.log("Found modules: " + entries.keySet(), Project.MSG_VERBOSE); 379 } 380 BINARY_SCAN_CACHE.put(build, entries); 381 } 382 return entries; 383 } 384 385 private static final String [] MODULE_DIRS = { 386 "modules", 387 "modules/eager", 388 "modules/autoload", 389 "lib", 390 "core", 391 }; 392 397 private static void doScanBinaries(File build, Map <String ,Entry> entries) throws IOException { 398 File [] clusters = build.listFiles(); 399 if (clusters == null) { 400 throw new IOException ("Cannot examine dir " + build); 401 } 402 for (int i = 0; i < clusters.length; i++) { 403 for (int j = 0; j < MODULE_DIRS.length; j++) { 404 File dir = new File (clusters[i], MODULE_DIRS[j].replace('/', File.separatorChar)); 405 if (!dir.isDirectory()) { 406 continue; 407 } 408 File [] jars = dir.listFiles(); 409 if (jars == null) { 410 throw new IOException ("Cannot examine dir " + dir); 411 } 412 for (int k = 0; k < jars.length; k++) { 413 File m = jars[k]; 414 if (!m.getName().endsWith(".jar")) { 415 continue; 416 } 417 JarFile jf = new JarFile (m); 418 try { 419 Attributes attr = jf.getManifest().getMainAttributes(); 420 String codename = attr.getValue("OpenIDE-Module"); 421 if (codename == null) { 422 continue; 423 } 424 String codenamebase; 425 int slash = codename.lastIndexOf('/'); 426 if (slash == -1) { 427 codenamebase = codename; 428 } else { 429 codenamebase = codename.substring(0, slash); 430 } 431 432 String cp = attr.getValue("Class-Path"); 433 File [] exts; 434 if (cp == null) { 435 exts = new File [0]; 436 } else { 437 String [] pieces = cp.split(" +"); 438 exts = new File [pieces.length]; 439 for (int l = 0; l < pieces.length; l++) { 440 exts[l] = new File (dir, pieces[l].replace('/', File.separatorChar)); 441 } 442 } 443 String moduleDependencies = attr.getValue("OpenIDE-Module-Module-Dependencies"); 444 445 446 Entry entry = new Entry(codenamebase, m, exts,dir, null, null, clusters[i].getName(),parseRuntimeDependencies(moduleDependencies)); 447 if (entries.containsKey(codenamebase)) { 448 throw new IOException ("Duplicated module " + codenamebase + ": found in " + entries.get(codenamebase) + " and " + entry); 449 } else { 450 entries.put(codenamebase, entry); 451 } 452 } finally { 453 jf.close(); 454 } 455 } 456 } 457 } 458 } 459 460 private static Map <String ,Entry> scanSuiteSources(Hashtable <String ,String > properties, Project project) throws IOException { 461 File basedir = new File (properties.get("basedir")); 462 String suiteDir = properties.get("suite.dir"); 463 if (suiteDir == null) { 464 throw new IOException ("No definition of suite.dir in " + basedir); 465 } 466 File suite = FileUtils.getFileUtils().resolveFile(basedir, suiteDir); 467 if (!suite.isDirectory()) { 468 throw new IOException ("No such suite " + suite); 469 } 470 Map <String ,Entry> entries = SUITE_SCAN_CACHE.get(suite); 471 if (entries == null) { 472 if (project != null) { 473 project.log("Scanning for modules in suite " + suite); 474 } 475 entries = new HashMap <String ,Entry>(); 476 doScanSuite(entries, suite, properties, project); 477 if (project != null) { 478 project.log("Found modules: " + entries.keySet(), Project.MSG_VERBOSE); 479 } 480 SUITE_SCAN_CACHE.put(suite, entries); 481 } 482 return entries; 483 } 484 485 private static void doScanSuite(Map <String ,Entry> entries, File suite, Hashtable <String ,String > properties, Project project) throws IOException { 486 Project fakeproj = new Project(); 487 fakeproj.setBaseDir(suite); Property faketask = new Property(); 489 faketask.setProject(fakeproj); 490 faketask.setFile(new File (suite, "nbproject/private/private.properties".replace('/', File.separatorChar))); 491 faketask.execute(); 492 faketask.setFile(new File (suite, "nbproject/project.properties".replace('/', File.separatorChar))); 493 faketask.execute(); 494 String modulesS = fakeproj.getProperty("modules"); 495 if (modulesS == null) { 496 throw new IOException ("No definition of modules in " + suite); 497 } 498 String [] modules = Path.translatePath(fakeproj, modulesS); 499 for (int i = 0; i < modules.length; i++) { 500 File module = new File (modules[i]); 501 if (!module.isDirectory()) { 502 throw new IOException ("No such module " + module + " referred to from " + suite); 503 } 504 if (!scanPossibleProject(module, entries, properties, null, ParseProjectXml.TYPE_SUITE, project)) { 505 throw new IOException ("No valid module found in " + module + " referred to from " + suite); 506 } 507 } 508 } 509 510 private static Entry scanStandaloneSource(Hashtable <String ,String > properties, Project project) throws IOException { 511 File basedir = new File (properties.get("project")); 512 Entry entry = STANDALONE_SCAN_CACHE.get(basedir); 513 if (entry == null) { 514 Map <String ,Entry> entries = new HashMap <String ,Entry>(); 515 if (!scanPossibleProject(basedir, entries, properties, null, ParseProjectXml.TYPE_STANDALONE, project)) { 516 throw new IOException ("No valid module found in " + basedir); 517 } 518 assert entries.size() == 1; 519 entry = entries.values().iterator().next(); 520 STANDALONE_SCAN_CACHE.put(basedir, entry); 521 } 522 return entry; 523 } 524 525 526 private final Map <String ,Entry> entries; 527 528 544 public ModuleListParser(Hashtable <String ,String > properties, int type, Project project) throws IOException { 545 String nball = properties.get("nb_all"); 546 if (type != ParseProjectXml.TYPE_NB_ORG) { 547 File basedir = new File (properties.get("basedir")); 549 if (nball != null) { 550 throw new IOException ("You must *not* declare <suite-component/> or <standalone/> for a netbeans.org module in " + basedir + "; fix project.xml to use the /2 schema"); 551 } 552 entries = scanBinaries(properties, project); 553 if (type == ParseProjectXml.TYPE_SUITE) { 554 entries.putAll(scanSuiteSources(properties, project)); 555 } else { 556 assert type == ParseProjectXml.TYPE_STANDALONE; 557 Entry e = scanStandaloneSource(properties, project); 558 entries.put(e.getCnb(), e); 559 } 560 } else { 561 if (nball == null) { 563 throw new IOException ("You must declare either <suite-component/> or <standalone/> for an external module in " + new File (properties.get("basedir"))); 564 } 565 boolean xtest = properties.get("xtest.home") != null && properties.get("xtest.testtype") != null; 567 if (properties.get("scan.binaries") != null || xtest) { 568 entries = scanBinaries(properties, project); 569 Entry e = scanStandaloneSource(properties, project); 571 if (e.clusterName == null && xtest) { 573 Entry oldEntry = entries.get(e.getCnb()); 574 if (oldEntry != null) { 575 e = new Entry(e.getCnb(),oldEntry.getJar(), 576 e.getClassPathExtensions(),e.sourceLocation, 577 e.netbeansOrgPath,e.buildPrerequisites, 578 oldEntry.getClusterName(), 579 e.runtimeDependencies); 580 } 581 } 582 entries.put(e.getCnb(), e); 583 } else { 584 entries = scanNetBeansOrgSources(new File (nball), properties, project); 585 } 586 } 587 } 588 592 public Set <Entry> findAll() { 593 return new HashSet <Entry>(entries.values()); 594 } 595 596 601 public Entry findByCodeNameBase(String cnb) { 602 return entries.get(cnb); 603 } 604 605 606 609 private static String [] parseRuntimeDependencies(String moduleDependencies) { 610 if (moduleDependencies == null) { 611 return new String [0]; 612 } 613 List <String > cnds = new ArrayList <String >(); 614 StringTokenizer toks = new StringTokenizer (moduleDependencies,","); 615 while (toks.hasMoreTokens()) { 616 String token = toks.nextToken().trim(); 617 int slIdx = token.indexOf('/'); 619 if (slIdx != -1) { 620 token = token.substring(0,slIdx); 621 } 622 slIdx = token.indexOf(' '); 624 if (slIdx != -1) { 625 token = token.substring(0,slIdx); 626 } 627 slIdx = token.indexOf('>'); 629 if (slIdx != -1) { 630 token = token.substring(0,slIdx); 631 } 632 token = token.trim(); 633 if (token.length() > 0) { 634 cnds.add(token); 635 } 636 } 637 return cnds.toArray(new String [cnds.size()]); 638 } 639 640 643 public static final class Entry { 644 645 private final String cnb; 646 private final File jar; 647 private final File [] classPathExtensions; 648 private final File sourceLocation; 649 private final String netbeansOrgPath; 650 private final String [] buildPrerequisites; 651 private final String clusterName; 652 private final String [] runtimeDependencies; 653 654 Entry(String cnb, File jar, File [] classPathExtensions, File sourceLocation, String netbeansOrgPath, String [] buildPrerequisites, String clusterName,String [] runtimeDependencies) { 655 this.cnb = cnb; 656 this.jar = jar; 657 this.classPathExtensions = classPathExtensions; 658 this.sourceLocation = sourceLocation; 659 this.netbeansOrgPath = netbeansOrgPath; 660 this.buildPrerequisites = buildPrerequisites; 661 this.clusterName = clusterName; 662 this.runtimeDependencies = runtimeDependencies; 663 } 664 665 668 public String getCnb() { 669 return cnb; 670 } 671 672 675 public File getJar() { 676 return jar; 677 } 678 679 682 public File [] getClassPathExtensions() { 683 return classPathExtensions; 684 } 685 686 689 public String getNetbeansOrgPath() { 690 return netbeansOrgPath; 691 } 692 693 697 public String [] getBuildPrerequisites() { 698 return buildPrerequisites; 699 } 700 702 public String [] getRuntimeDependencies() { 703 return runtimeDependencies; 704 } 705 706 714 public String getClusterName() { 715 return clusterName; 716 } 717 718 public String toString() { 719 return (sourceLocation != null ? sourceLocation : jar).getAbsolutePath(); 720 } 721 722 } 723 724 } 725 | Popular Tags |