PageRenderTime 39ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/nbbuild/antsrc/org/netbeans/nbbuild/ModuleDependencies.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1207 lines | 1023 code | 99 blank | 85 comment | 315 complexity | 7fac4d7de2dae6ce465b23a2ed4ba4db MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.nbbuild;
  45. import java.io.BufferedReader;
  46. import java.io.File;
  47. import java.io.FileReader;
  48. import java.io.FileWriter;
  49. import java.io.IOException;
  50. import java.io.InputStream;
  51. import java.io.PrintWriter;
  52. import java.text.Collator;
  53. import java.util.ArrayList;
  54. import java.util.Collection;
  55. import java.util.Collections;
  56. import java.util.Enumeration;
  57. import java.util.HashMap;
  58. import java.util.HashSet;
  59. import java.util.LinkedHashSet;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.Properties;
  63. import java.util.Set;
  64. import java.util.SortedMap;
  65. import java.util.SortedSet;
  66. import java.util.StringTokenizer;
  67. import java.util.TreeMap;
  68. import java.util.TreeSet;
  69. import java.util.jar.JarEntry;
  70. import java.util.jar.JarFile;
  71. import java.util.jar.Manifest;
  72. import java.util.regex.Pattern;
  73. import org.apache.tools.ant.BuildException;
  74. import org.apache.tools.ant.DirectoryScanner;
  75. import org.apache.tools.ant.Project;
  76. import org.apache.tools.ant.Task;
  77. import org.apache.tools.ant.types.EnumeratedAttribute;
  78. import org.apache.tools.ant.types.FileSet;
  79. /** This task implements the module dependencies verification proposal
  80. * that is described at
  81. * http://openide.netbeans.org/proposals/arch/clusters.html#verify-solution
  82. */
  83. public class ModuleDependencies extends Task {
  84. private List<Input> inputs = new ArrayList<Input>();
  85. private List<Output> outputs = new ArrayList<Output>();
  86. private Set<ModuleInfo> modules;
  87. private Pattern regexp;
  88. public ModuleDependencies () {
  89. }
  90. public void setGenerate(String regexpList) {
  91. regexp = Pattern.compile(regexpList);
  92. }
  93. public Input createInput() throws BuildException {
  94. Input input = new Input ();
  95. inputs.add (input);
  96. return input;
  97. }
  98. public void addConfiguredInputPattern(InputPattern pattern) throws BuildException {
  99. inputs.addAll(pattern.inputs());
  100. }
  101. public Output createOutput() throws BuildException {
  102. Output output = new Output ();
  103. outputs.add (output);
  104. return output;
  105. }
  106. public @Override void execute() throws BuildException {
  107. if (outputs.size () == 0) throw new BuildException ("At least one <output> tag has to be specified");
  108. try {
  109. readModuleInfo ();
  110. for (Output o : outputs) {
  111. if (o.type == null) throw new BuildException ("<output> needs attribute type");
  112. if (o.file == null) throw new BuildException ("<output> needs attribute file");
  113. if ("public-packages".equals (o.type.getValue ())) {
  114. generatePublicPackages (o.file, true, false);
  115. } else if ("friend-packages".equals (o.type.getValue ())) {
  116. generatePublicPackages (o.file, false, false);
  117. } else if ("shared-packages".equals (o.type.getValue ())) {
  118. generateSharedPackages (o.file);
  119. } else if ("modules".equals (o.type.getValue ())) {
  120. generateListOfModules (o.file);
  121. } else if ("disabled-autoloads".equals(o.type.getValue())) {
  122. generateListOfDisabledAutoloads(o.file);
  123. } else if ("dependencies".equals (o.type.getValue ())) {
  124. generateDependencies (o.file, false);
  125. } else if ("implementation-dependencies".equals (o.type.getValue ())) {
  126. generateDependencies (o.file, true);
  127. } else if ("group-dependencies".equals (o.type.getValue ())) {
  128. generateGroupDependencies (o.file, false);
  129. } else if ("group-implementation-dependencies".equals (o.type.getValue ())) {
  130. generateGroupDependencies (o.file, true);
  131. } else if ("group-friend-packages".equals (o.type.getValue ())) {
  132. generatePublicPackages(o.file, false, true);
  133. } else if ("kits".equals(o.type.getValue())) {
  134. generateKits(o.file);
  135. } else if ("kit-dependencies".equals(o.type.getValue())) {
  136. generateKitDependencies(o.file);
  137. } else if ("plugins".equals(o.type.getValue())) {
  138. generatePlugins(o.file);
  139. } else if ("reverse-dependencies".equals(o.type.getValue())) {
  140. generateReverseDependencies(o.file);
  141. } else {
  142. assert false : o.type;
  143. }
  144. getProject().log(o.file + ": generating " + o.type);
  145. }
  146. } catch (IOException ex) {
  147. throw new BuildException (ex);
  148. }
  149. }
  150. private void readModuleInfo () throws IOException {
  151. modules = new TreeSet<ModuleInfo>();
  152. if (inputs.isEmpty()) {
  153. throw new BuildException ("At least one <input> tag is needed");
  154. }
  155. for (Input input : inputs) {
  156. if (input.jars == null) throw new BuildException ("<input> needs a subelement <jars>");
  157. if (input.name == null) throw new BuildException ("<input> needs attribute name");
  158. Project p = getProject();
  159. DirectoryScanner scan = input.jars.getDirectoryScanner(p);
  160. for (String incl : scan.getIncludedFiles()) {
  161. File f = new File(scan.getBasedir(), incl);
  162. getProject().log("Processing " + f, Project.MSG_VERBOSE);
  163. JarFile file = new JarFile (f);
  164. Manifest manifest = file.getManifest();
  165. if (manifest == null) {
  166. // process only manifest files
  167. continue;
  168. }
  169. final boolean[] osgi = new boolean[1];
  170. String module = JarWithModuleAttributes.extractCodeName(manifest.getMainAttributes(), osgi);
  171. if (module == null) {
  172. // skip this one
  173. continue;
  174. }
  175. ModuleInfo m;
  176. {
  177. String codebasename;
  178. int majorVersion;
  179. // base name
  180. int slash = module.indexOf ('/');
  181. if (slash == -1) {
  182. codebasename = module;
  183. majorVersion = -1;
  184. } else {
  185. codebasename = module.substring (0, slash);
  186. majorVersion = Integer.valueOf(module.substring(slash + 1));
  187. }
  188. m = new ModuleInfo (input.name, f, codebasename);
  189. m.majorVersion = majorVersion;
  190. }
  191. String lb = file.getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle");
  192. if (lb != null) {
  193. Properties props = new Properties();
  194. InputStream is = file.getInputStream(file.getEntry(lb));
  195. try {
  196. props.load(is);
  197. } finally {
  198. is.close();
  199. }
  200. m.displayName = props.getProperty("OpenIDE-Module-Name");
  201. m.displayCategory = props.getProperty("OpenIDE-Module-Display-Category");
  202. }
  203. // XXX if osgi[0], instead load Export-Package, Require-Bundle, Bundle-Version... ought to be some utility class to interconvert NB & OSGi manifests!
  204. m.publicPackages = file.getManifest ().getMainAttributes ().getValue ("OpenIDE-Module-Public-Packages");
  205. {
  206. m.specificationVersion = file.getManifest ().getMainAttributes ().getValue ("OpenIDE-Module-Specification-Version");
  207. }
  208. m.implementationVersion = file.getManifest ().getMainAttributes ().getValue ("OpenIDE-Module-Implementation-Version");
  209. TreeSet<Dependency> depends = new TreeSet<Dependency>();
  210. TreeSet<Dependency> provides = new TreeSet<Dependency>();
  211. addDependencies (depends, file.getManifest (), Dependency.Type.REQUIRES, "OpenIDE-Module-Requires");
  212. addDependencies (depends, file.getManifest (), Dependency.Type.REQUIRES, "OpenIDE-Module-Needs");
  213. addDependencies (depends, file.getManifest (), Dependency.Type.RECOMMENDS, "OpenIDE-Module-Recommends");
  214. addDependencies (provides, file.getManifest (), /*irrelevant*/Dependency.Type.REQUIRES, "OpenIDE-Module-Provides");
  215. {
  216. String ideDeps = file.getManifest ().getMainAttributes ().getValue ("OpenIDE-Module-IDE-Dependencies"); // IDE/1 > 4.25
  217. if (ideDeps != null) {
  218. throw new BuildException("OpenIDE-Module-IDE-Dependencies is obsolete in " + f);
  219. }
  220. }
  221. addDependencies (depends, file.getManifest (), Dependency.Type.DIRECT, "OpenIDE-Module-Module-Dependencies");
  222. /* org.netbeans.api.java/1,org.netbeans.modules.queries/0,
  223. org.netbeans.modules.javacore/1,org.netbeans.jmi.javamodel/1 > 1.11,org.netbeans.api.mdr/1,
  224. org.netbeans.modules.mdr/1= 1.0.0,org.netbeans.modules.
  225. jmiutils/1 = 1.0.0,javax.jmi.reflect/1,
  226. org.openide.loaders,org.openide.src > 1.0
  227. */
  228. m.depends = depends;
  229. m.provides = new HashSet<String>();
  230. for (Dependency d : provides) {
  231. m.provides.add(d.getName());
  232. }
  233. {
  234. String friends = file.getManifest ().getMainAttributes ().getValue ("OpenIDE-Module-Friends");
  235. if (friends != null) {
  236. TreeSet<String> set = new TreeSet<String>();
  237. StringTokenizer tok = new StringTokenizer(friends, ", ");
  238. while (tok.hasMoreElements()) {
  239. set.add(tok.nextToken());
  240. }
  241. m.friends = set;
  242. }
  243. }
  244. String essential = file.getManifest ().getMainAttributes ().getValue ("AutoUpdate-Essential-Module");
  245. m.isEssential = essential == null ?
  246. false :
  247. Boolean.parseBoolean(file.getManifest().getMainAttributes().getValue("AutoUpdate-Essential-Module"));
  248. m.isAutoload = determineParameter(f, "autoload");
  249. m.isEager = determineParameter(f, "eager");
  250. String showInAutoUpdate = file.getManifest().getMainAttributes().getValue("AutoUpdate-Show-In-Client");
  251. if (showInAutoUpdate == null) {
  252. m.showInAutoupdate = !m.isAutoload && !m.isEager;
  253. } else {
  254. m.showInAutoupdate = Boolean.parseBoolean(showInAutoUpdate);
  255. }
  256. modules.add (m);
  257. }
  258. }
  259. }
  260. private boolean determineParameter(File moduleFile, String parameter) throws IOException {
  261. String name = moduleFile.getName();
  262. name = name.substring(0, name.length() - 3) + "xml";
  263. File configFile = new File(moduleFile.getParentFile().getParentFile(), "config/Modules/" + name);
  264. log ("config " + configFile, Project.MSG_DEBUG);
  265. if (!configFile.exists())
  266. return true; // probably a classpath module, treat like autoload
  267. final String fragment = "<param name=\"" + parameter + "\">true</param>";
  268. BufferedReader br = new BufferedReader (new FileReader (configFile));
  269. try {
  270. String line;
  271. while ((line = br.readLine ()) != null) {
  272. if (line.indexOf (fragment) != -1) {
  273. log ("autoload module: " + moduleFile, Project.MSG_DEBUG);
  274. return true;
  275. }
  276. }
  277. } finally {
  278. br.close ();
  279. }
  280. return false;
  281. }
  282. private void generatePublicPackages(File output, boolean justPublic, boolean justInterCluster) throws BuildException, IOException {
  283. TreeSet<String> packages = new TreeSet<String>();
  284. TreeMap<ModuleInfo,TreeSet<String>> friendExports = new TreeMap<ModuleInfo,TreeSet<String>>();
  285. {
  286. for (ModuleInfo m : modules) {
  287. if (justPublic) {
  288. if (m.friends != null) {
  289. continue;
  290. }
  291. }
  292. if (regexp != null && !regexp.matcher(m.group).matches()) {
  293. continue;
  294. }
  295. String s = m.publicPackages;
  296. Map<String,Boolean> pkgs = null;
  297. if (s != null) {
  298. pkgs = new HashMap<String,Boolean>();
  299. StringTokenizer tok = new StringTokenizer(s, ",");
  300. while (tok.hasMoreElements()) {
  301. String p = tok.nextToken().trim();
  302. if (p.equals("-")) {
  303. continue;
  304. }
  305. if (p.endsWith(".*")) {
  306. pkgs.put(p.substring(0, p.length() - 2).replace('.', '/'), Boolean.FALSE);
  307. continue;
  308. }
  309. if (p.endsWith(".**")) {
  310. pkgs.put(p.substring(0, p.length() - 3).replace('.', '/'), Boolean.TRUE);
  311. continue;
  312. }
  313. throw new BuildException("Unknown package format: " + p + " in " + m.file);
  314. }
  315. }
  316. if (justPublic) {
  317. iterateThruPackages(m.file, pkgs, packages);
  318. if (pkgs != null && packages.size() < pkgs.size()) {
  319. throw new BuildException("Not enough packages found. The declared packages are: " + s + " but only " + packages + " were found in " + m.file);
  320. }
  321. } else {
  322. TreeSet<String> modulePkgs = new TreeSet<String>();
  323. iterateThruPackages(m.file, pkgs, modulePkgs);
  324. friendExports.put(m, modulePkgs);
  325. }
  326. }
  327. }
  328. PrintWriter w = new PrintWriter(new FileWriter(output));
  329. if (justPublic) {
  330. for (String out : packages) {
  331. w.println(out.replace('/', '.'));
  332. }
  333. } else {
  334. int maxFriends = Integer.MAX_VALUE;
  335. if (justInterCluster) {
  336. String maxFriendsString = this.getProject().getProperty("deps.max.friends");
  337. if (maxFriendsString != null) {
  338. maxFriends = Integer.parseInt(maxFriendsString);
  339. }
  340. }
  341. for (Map.Entry<ModuleInfo,TreeSet<String>> entry : friendExports.entrySet()) {
  342. ModuleInfo info = entry.getKey();
  343. if (info.friends == null) {
  344. continue;
  345. }
  346. log("Friends for " + info.getName(false), Project.MSG_DEBUG);
  347. int cntFriends = 0;
  348. boolean printed = false;
  349. for (String n : info.friends) {
  350. ModuleInfo friend = findModuleInfo(n);
  351. if (justInterCluster && friend != null && friend.group.equals(info.group)) {
  352. continue;
  353. }
  354. if (!printed) {
  355. w.print("MODULE ");
  356. w.println(info.getName(false));
  357. printed = true;
  358. }
  359. if (friend != null) {
  360. w.print(" FRIEND ");
  361. w.println(friend.getName(false));
  362. } else {
  363. w.print(" EXTERNAL ");
  364. w.println(n);
  365. }
  366. cntFriends++;
  367. }
  368. if (cntFriends > maxFriends) {
  369. w.println(" WARNING: excessive number of intercluster friends (" + cntFriends + ")");
  370. }
  371. if (cntFriends > 0) {
  372. for (String out : entry.getValue()) {
  373. w.print(" PACKAGE ");
  374. w.println(out.replace('/', '.'));
  375. }
  376. }
  377. }
  378. }
  379. w.close();
  380. }
  381. private void iterateThruPackages(File f, Map<String,Boolean> pkgs, TreeSet<String> packages) throws IOException {
  382. JarFile file = new JarFile (f);
  383. Enumeration en = file.entries ();
  384. LOOP: while (en.hasMoreElements ()) {
  385. JarEntry e = (JarEntry)en.nextElement ();
  386. if (e.getName ().endsWith (".class")) {
  387. int last = e.getName ().lastIndexOf ('/');
  388. if (last == -1) {
  389. // skip default pkg
  390. continue;
  391. }
  392. String p = e.getName ().substring (0, last);
  393. if (pkgs == null) {
  394. packages.add (p);
  395. continue;
  396. }
  397. Boolean b = pkgs.get(p);
  398. if (b != null) {
  399. packages.add (p);
  400. continue;
  401. }
  402. String parent = p;
  403. while (parent.length () > 0) {
  404. int prev = parent.lastIndexOf ('/');
  405. if (prev == -1) {
  406. parent = "";
  407. } else {
  408. parent = parent.substring (0, prev);
  409. }
  410. b = pkgs.get(parent);
  411. if (Boolean.TRUE.equals (b)) {
  412. packages.add (p);
  413. continue LOOP;
  414. }
  415. }
  416. }
  417. }
  418. java.util.jar.Manifest m = file.getManifest ();
  419. if (m != null) {
  420. String value = m.getMainAttributes ().getValue ("Class-Path");
  421. if (value != null) {
  422. StringTokenizer tok = new StringTokenizer (value, " ");
  423. while (tok.hasMoreElements ()) {
  424. File sub = new File (f.getParentFile (), tok.nextToken ());
  425. if (sub.isFile ()) {
  426. iterateThruPackages (sub, pkgs, packages);
  427. }
  428. }
  429. }
  430. }
  431. file.close ();
  432. }
  433. private void generateListOfModules (File output) throws BuildException, IOException {
  434. PrintWriter w = new PrintWriter (new FileWriter (output));
  435. for (ModuleInfo m : modules) {
  436. if (regexp != null && !regexp.matcher(m.group).matches()) {
  437. continue;
  438. }
  439. w.print ("MODULE ");
  440. w.print(m.getName(true));
  441. w.println ();
  442. }
  443. w.close ();
  444. }
  445. private void generateListOfDisabledAutoloads(File output) throws BuildException, IOException {
  446. Map<String,Set<String>> depsAll = new TreeMap<String,Set<String>>();
  447. Map<String,ModuleInfo> considered = new TreeMap<String,ModuleInfo>();
  448. Set<String> regular = new HashSet<String>();
  449. for (ModuleInfo m : modules) {
  450. if (regexp != null && !regexp.matcher(m.group).matches()) {
  451. continue;
  452. }
  453. if (m.isAutoload) {
  454. considered.put(m.codebasename, m);
  455. } else if (!m.isEager) {
  456. regular.add(m.codebasename);
  457. }
  458. Set<String> deps = new TreeSet<String>();
  459. depsAll.put(m.codebasename, deps);
  460. for (Dependency d : m.depends) {
  461. for (ModuleInfo m2 : findModuleInfo(d, m)) {
  462. deps.add(m2.codebasename);
  463. }
  464. }
  465. }
  466. transitiveClosure(depsAll);
  467. Map<String,Set<ModuleInfo>> disabled = new TreeMap<String,Set<ModuleInfo>>();
  468. for (Map.Entry<String, Set<String>> entry : depsAll.entrySet()) {
  469. if (!regular.contains(entry.getKey())) {
  470. continue;
  471. }
  472. for (String dep : entry.getValue()) {
  473. considered.remove(dep);
  474. }
  475. }
  476. for (ModuleInfo m : considered.values()) {
  477. Set<ModuleInfo> group = disabled.get(m.group);
  478. if (group == null) {
  479. group = new TreeSet<ModuleInfo>();
  480. disabled.put(m.group, group);
  481. }
  482. group.add(m);
  483. }
  484. PrintWriter w = new PrintWriter(new FileWriter(output));
  485. for (Set<ModuleInfo> group : disabled.values()) {
  486. for (ModuleInfo m : group) {
  487. w.print("MODULE ");
  488. w.print(m.getName(false));
  489. w.println();
  490. }
  491. }
  492. w.close();
  493. }
  494. private void generateKits(File output) throws BuildException, IOException {
  495. PrintWriter w = new PrintWriter(new FileWriter(output));
  496. // calculate transitive closure of kits
  497. TreeMap<String, TreeSet<String>> allKitDeps = transitiveClosureOfKits();
  498. // calculate transitive closure of modules
  499. TreeMap<String, TreeSet<String>> allModuleDeps = transitiveClosureOfModules();
  500. // create a map of <module, kits that depend on it>
  501. TreeMap<ModuleInfo, Set<String>> dependingKits = new TreeMap<ModuleInfo, Set<String>>();
  502. for (ModuleInfo m : modules) {
  503. if (regexp != null && !regexp.matcher(m.group).matches()) {
  504. continue;
  505. }
  506. if (m.showInAutoupdate) {
  507. // this is a kit
  508. Set<String> dep = allModuleDeps.get(m.codebasename);
  509. for (String ds : dep) {
  510. if (regexp != null && !regexp.matcher(m.group).matches()) {
  511. continue;
  512. }
  513. //log ("ds " + dep);
  514. ModuleInfo theModuleOneIsDependingOn = findModuleInfo(ds);
  515. if (!theModuleOneIsDependingOn.showInAutoupdate &&
  516. !theModuleOneIsDependingOn.isAutoload &&
  517. !theModuleOneIsDependingOn.isEager &&
  518. !theModuleOneIsDependingOn.isEssential) {
  519. // regular module, not a kit
  520. Set<String> kits = dependingKits.get(theModuleOneIsDependingOn);
  521. if (kits == null) {
  522. kits = new TreeSet<String>();
  523. dependingKits.put(theModuleOneIsDependingOn, kits);
  524. }
  525. kits.add(m.getName(false));
  526. // w.print(" REQUIRES " + theModuleOneIsDependingOn.getName());
  527. // w.println();
  528. }
  529. }
  530. }
  531. }
  532. // now check that there is one canonical kit that "contains" the module
  533. // at the same time create a map of <kit, set of <module>>
  534. TreeMap<String, TreeSet<String>> allKits = new TreeMap<String, TreeSet<String>>();
  535. for (ModuleInfo module : dependingKits.keySet()) {
  536. Set<String> kits = dependingKits.get(module);
  537. // candidate for the lowest kit
  538. String lowestKitCandidate = null;
  539. for (String kit : kits) {
  540. if (lowestKitCandidate == null) {
  541. lowestKitCandidate = kit;
  542. log (" initial lowest kit candidate for " + module.getName(false) + " : " +
  543. lowestKitCandidate, Project.MSG_DEBUG);
  544. }
  545. else {
  546. if (dependsOnTransitively(lowestKitCandidate, kit, allKitDeps)) {
  547. lowestKitCandidate = kit;
  548. log (" new lowest kit candidate for " + module.getName(false) + " : " +
  549. lowestKitCandidate, Project.MSG_DEBUG);
  550. }
  551. }
  552. }
  553. // check that all kits depend on the lowest kit candidate
  554. boolean passed = true;
  555. for (String kit : kits) {
  556. if (!kit.equals(lowestKitCandidate) &&
  557. !dependsOnTransitively(kit, lowestKitCandidate, allKitDeps)) {
  558. log ("lowest kit not found for " + module.getName(false) + " : " +
  559. lowestKitCandidate + ", " + kit + " do not have a dependency", Project.MSG_VERBOSE);
  560. passed = false;
  561. break;
  562. }
  563. }
  564. if (passed) {
  565. dependingKits.put(module, Collections.singleton(lowestKitCandidate));
  566. registerModuleInKit(module, lowestKitCandidate, allKits);
  567. } else {
  568. w.print("Warning: ambiguous module ownership - module ");
  569. w.print(module.getName(false));
  570. w.print(" is contained in kits ");
  571. w.println();
  572. for (String kit : kits) {
  573. registerModuleInKit(module, kit, allKits);
  574. w.print(" " + kit);
  575. w.println();
  576. }
  577. w.println("No dependency between ");
  578. for (String kit : kits) {
  579. if (!kit.equals(lowestKitCandidate) &&
  580. !dependsOnTransitively(kit, lowestKitCandidate, allKitDeps)) {
  581. w.println (" " + lowestKitCandidate + ", " + kit);
  582. }
  583. }
  584. }
  585. }
  586. // now actually print out the kit contents
  587. for (String kit : allKits.keySet()) {
  588. w.print("KIT ");
  589. w.print(kit);
  590. w.println();
  591. for (String m : allKits.get(kit)) {
  592. w.print(" CONTAINS " + m);
  593. w.println();
  594. }
  595. }
  596. w.close();
  597. }
  598. private boolean dependsOnTransitively(String kit1, String kit2,
  599. TreeMap<String, TreeSet<String>> dependingKits) {
  600. TreeSet kits = dependingKits.get(kit1);
  601. if (kits == null) {
  602. return false;
  603. }
  604. return kits.contains(kit2);
  605. }
  606. private static void registerModuleInKit(ModuleInfo module, String kit, TreeMap<String, TreeSet<String>> allKits) {
  607. TreeSet<String> modules = allKits.get(kit);
  608. if (modules == null) {
  609. modules = new TreeSet<String>();
  610. allKits.put(kit, modules);
  611. }
  612. modules.add(module.getName(false));
  613. }
  614. private TreeMap<String, TreeSet<String>> transitiveClosureOfModules() {
  615. TreeMap<String, TreeSet<String>> moduleDepsAll = new TreeMap<String, TreeSet<String>>();
  616. // populate with modules first
  617. for (ModuleInfo m : modules) {
  618. TreeSet<String> deps = new TreeSet<String>();
  619. moduleDepsAll.put(m.codebasename, deps);
  620. for (Dependency d : m.depends) {
  621. for (ModuleInfo theModuleOneIsDependingOn : findModuleInfo(d, m)) {
  622. deps.add(theModuleOneIsDependingOn.codebasename);
  623. }
  624. }
  625. }
  626. transitiveClosure(moduleDepsAll);
  627. return moduleDepsAll;
  628. }
  629. private TreeMap<String, TreeSet<String>> transitiveClosureOfKits() {
  630. TreeMap<String, TreeSet<String>> kitDepsAll = new TreeMap<String, TreeSet<String>>();
  631. // populate with kits first
  632. for (ModuleInfo m : modules) {
  633. if (m.showInAutoupdate) {
  634. TreeSet<String> deps = new TreeSet<String>();
  635. kitDepsAll.put(m.getName(false), deps);
  636. for (Dependency d : m.depends) {
  637. for (ModuleInfo theModuleOneIsDependingOn : findModuleInfo(d, m)) {
  638. if (theModuleOneIsDependingOn.showInAutoupdate) {
  639. deps.add(theModuleOneIsDependingOn.getName(false));
  640. }
  641. }
  642. }
  643. }
  644. }
  645. transitiveClosure(kitDepsAll);
  646. return kitDepsAll;
  647. }
  648. /** Computes the transitive closure of the dependency map passed as a parameter.
  649. *
  650. * @param deps the dependency map, will contain the transitive closure when the method exits
  651. */
  652. private <T> void transitiveClosure(Map<T,? extends Set<T>> allDeps) {
  653. boolean needAnotherIteration = true;
  654. while (needAnotherIteration) {
  655. needAnotherIteration = false;
  656. for (Map.Entry<T,? extends Set<T>> entry : allDeps.entrySet()) {
  657. Set<T> deps = entry.getValue();
  658. for (T d : new TreeSet<T>(deps)) {
  659. for (T d2: allDeps.get(d)) {
  660. if (deps.add(d2)) {
  661. log("transitive closure: need to add " + d2 + " to " + entry.getKey(), Project.MSG_DEBUG);
  662. needAnotherIteration = true;
  663. }
  664. }
  665. }
  666. }
  667. }
  668. }
  669. private void generateKitDependencies(File output) throws BuildException, IOException {
  670. PrintWriter w = new PrintWriter(new FileWriter(output));
  671. for (ModuleInfo m : modules) {
  672. if (regexp != null && !regexp.matcher(m.group).matches()) {
  673. continue;
  674. }
  675. if (m.showInAutoupdate) {
  676. w.print("KIT ");
  677. w.print(m.getName(false));
  678. w.println();
  679. for (Dependency d : m.depends) {
  680. if (regexp != null && !regexp.matcher(m.group).matches()) {
  681. continue;
  682. }
  683. for (ModuleInfo theModuleOneIsDependingOn : findModuleInfo(d, m)) {
  684. if (theModuleOneIsDependingOn.showInAutoupdate) {
  685. w.print(" REQUIRES " + theModuleOneIsDependingOn.getName(false));
  686. w.println();
  687. }
  688. }
  689. }
  690. }
  691. }
  692. w.close();
  693. }
  694. private void generatePlugins(File output) throws BuildException, IOException {
  695. Set<String> standardClusters = new HashSet<String>();
  696. String standardClustersS = getProject().getProperty("clusters.config.full.list");
  697. if (standardClustersS != null) {
  698. for (String clusterProp : standardClustersS.split(",")) {
  699. String dir = getProject().getProperty(clusterProp + ".dir");
  700. if (dir != null) {
  701. standardClusters.add(dir.replaceFirst("[0-9.]+$", ""));
  702. }
  703. }
  704. }
  705. FileWriter fw = new FileWriter(output);
  706. try {
  707. PrintWriter w = new PrintWriter(fw);
  708. SortedMap<String,String> lines = new TreeMap<String,String>(Collator.getInstance());
  709. lines.put("A", "||Code Name Base||Display Name||Display Category||Standard Cluster");
  710. lines.put("C", "");
  711. lines.put("D", "||Code Name Base||Display Name||Display Category||Extra Cluster");
  712. for (ModuleInfo m : modules) {
  713. if (regexp != null && !regexp.matcher(m.group).matches()) {
  714. continue;
  715. }
  716. if (m.showInAutoupdate) {
  717. lines.put((standardClusters.contains(m.group) ? "B" : "E") + m.displayCategory + " " + m.displayName,
  718. "|" + m.codebasename + "|" + m.displayName + "|" + m.displayCategory + "|" + m.group);
  719. }
  720. }
  721. for (String line : lines.values()) {
  722. w.println(line);
  723. }
  724. w.flush();
  725. } finally {
  726. fw.close();
  727. }
  728. }
  729. private void generateReverseDependencies(File output) throws BuildException, IOException {
  730. FileWriter fw = new FileWriter(output);
  731. try {
  732. PrintWriter w = new PrintWriter(fw);
  733. for (ModuleInfo m : modules) {
  734. if (m.group.equals("extra")) {
  735. continue;
  736. }
  737. String clusterDeps = getProject().getProperty("nb.cluster." + m.group + ".depends");
  738. if (clusterDeps == null) {
  739. throw new BuildException("no property ${nb.cluster." + m.group + ".depends} defined");
  740. }
  741. Set<String> allowed = new HashSet<String>();
  742. allowed.add(m.group);
  743. for (String piece : clusterDeps.split(",")) {
  744. allowed.add(piece.replaceFirst("^nb[.]cluster[.]", ""));
  745. }
  746. for (Dependency d : m.depends) {
  747. if (d.type == Dependency.Type.RECOMMENDS) {
  748. continue;
  749. }
  750. for (ModuleInfo o : findModuleInfo(d, m)) {
  751. if (o.codebasename.equals("org.netbeans.libs.junit4")) {
  752. continue; // special case
  753. }
  754. if (!allowed.contains(o.group)) {
  755. w.println(m.getName(false) + " -> " + o.getName(false));
  756. }
  757. }
  758. }
  759. }
  760. w.flush();
  761. } finally {
  762. fw.close();
  763. }
  764. }
  765. private void generateSharedPackages (File output) throws BuildException, IOException {
  766. TreeMap<String,List<ModuleInfo>> packages = new TreeMap<String,List<ModuleInfo>>();
  767. for (ModuleInfo m : modules) {
  768. HashSet<String> pkgs = new HashSet<String>();
  769. iterateSharedPackages(m.file, pkgs);
  770. for (String s : pkgs) {
  771. List<ModuleInfo> l = packages.get(s);
  772. if (l == null) {
  773. l = new ArrayList<ModuleInfo>();
  774. packages.put(s, l);
  775. }
  776. l.add(m);
  777. }
  778. }
  779. PrintWriter w = new PrintWriter (new FileWriter (output));
  780. for (Map.Entry<String,List<ModuleInfo>> entry : packages.entrySet()) {
  781. String pkg = entry.getKey().replace('/', '.');
  782. if (pkg.equals("")) {
  783. continue; // ignore default package
  784. }
  785. List<ModuleInfo> cnt = entry.getValue();
  786. if (cnt.size() > 1) {
  787. SortedSet<String> cnbs = new TreeSet<String>();
  788. for (ModuleInfo m : cnt) {
  789. if (regexp == null || regexp.matcher(m.group).matches()) {
  790. cnbs.add(m.codebasename);
  791. }
  792. }
  793. if (cnbs.size() > 1) {
  794. w.println("PACKAGE " + pkg);
  795. for (String cnb : cnbs) {
  796. w.println(" MODULE " + cnb);
  797. }
  798. }
  799. }
  800. }
  801. w.close ();
  802. }
  803. private void iterateSharedPackages (File f, Set<String> myPkgs) throws IOException {
  804. JarFile file = new JarFile (f);
  805. Enumeration<JarEntry> en = file.entries ();
  806. LOOP: while (en.hasMoreElements ()) {
  807. JarEntry e = en.nextElement ();
  808. if (e.getName ().endsWith ("/")) {
  809. continue;
  810. }
  811. if (e.getName ().startsWith ("META-INF/")) {
  812. continue;
  813. }
  814. int last = e.getName ().lastIndexOf ('/');
  815. String pkg = last == -1 ? "" : e.getName ().substring (0, last);
  816. myPkgs.add (pkg);
  817. log("Found package " + pkg + " in " + f, Project.MSG_DEBUG);
  818. }
  819. Manifest m = file.getManifest();
  820. if (m != null) {
  821. String value = m.getMainAttributes ().getValue ("Class-Path");
  822. if (value != null) {
  823. StringTokenizer tok = new StringTokenizer (value, " ");
  824. while (tok.hasMoreElements ()) {
  825. File sub = new File (f.getParentFile (), tok.nextToken ());
  826. if (sub.isFile ()) {
  827. iterateSharedPackages (sub, myPkgs);
  828. }
  829. }
  830. }
  831. }
  832. file.close ();
  833. }
  834. private void generateDependencies (File output, boolean implementationOnly) throws BuildException, IOException {
  835. PrintWriter w = new PrintWriter (new FileWriter (output));
  836. for (ModuleInfo m : modules) {
  837. boolean first = true;
  838. Set<ModuleInfo> written = new HashSet<ModuleInfo>(); // XXX needed for other uses of findModuleInfo too
  839. for (Dependency d : m.depends) {
  840. if (d.getName().startsWith("org.openide.modules.ModuleFormat")) {
  841. continue; // just clutter
  842. }
  843. String print = d.type == Dependency.Type.RECOMMENDS ? " RECOMMENDS " : " REQUIRES ";
  844. if (d.exact && d.compare != null) {
  845. // ok, impl deps
  846. } else {
  847. if (implementationOnly) {
  848. continue;
  849. }
  850. }
  851. if (regexp != null && !regexp.matcher(m.group).matches()) {
  852. continue;
  853. }
  854. if (first) {
  855. w.print ("MODULE ");
  856. w.println(m.getName (false));
  857. first = false;
  858. }
  859. if (d.isSpecial ()) {
  860. w.print(print);
  861. w.println(d.getName ());
  862. } else {
  863. for (ModuleInfo theModuleOneIsDependingOn : findModuleInfo(d, m)) {
  864. if (written.add(theModuleOneIsDependingOn)) {
  865. w.print(print);
  866. w.println(theModuleOneIsDependingOn.getName(false));
  867. }
  868. }
  869. }
  870. }
  871. }
  872. w.close ();
  873. }
  874. private void generateGroupDependencies (File output, boolean implementationOnly) throws BuildException, IOException {
  875. PrintWriter w = new PrintWriter (new FileWriter (output));
  876. Map<Dependency,Set<ModuleInfo>> referrers = new HashMap<Dependency,Set<ModuleInfo>>();
  877. TreeMap<String, Set<Dependency>> groups = new TreeMap<String, Set<Dependency>>();
  878. for (ModuleInfo m : modules) {
  879. if (regexp != null && !regexp.matcher(m.group).matches()) {
  880. continue;
  881. }
  882. Set<Dependency> l = groups.get(m.group);
  883. if (l == null) {
  884. l = new TreeSet<Dependency>();
  885. groups.put(m.group, l);
  886. }
  887. Set<Dependency> deps = new HashSet<Dependency>();
  888. for (Dependency d : m.depends) {
  889. if (implementationOnly && (!d.exact || d.compare == null)) {
  890. continue;
  891. }
  892. // special dependencies are ignored
  893. if (d.isSpecial ()) {
  894. continue;
  895. }
  896. deps.add(d);
  897. }
  898. l.addAll(deps);
  899. for (Dependency d : deps) {
  900. Set<ModuleInfo> r = referrers.get(d);
  901. if (r == null) {
  902. r = new HashSet<ModuleInfo>();
  903. referrers.put(d, r);
  904. }
  905. r.add(m);
  906. }
  907. }
  908. for (Map.Entry<String,Set<Dependency>> e : groups.entrySet()) {
  909. String groupName = e.getKey();
  910. Set<Dependency> depends = e.getValue();
  911. boolean first = true;
  912. for (Dependency d : depends) {
  913. String print = d.type == Dependency.Type.RECOMMENDS ? " RECOMMENDS ": " REQUIRES ";
  914. // dependencies within one group are not important
  915. Set<ModuleInfo> r = referrers.get(d);
  916. for (ModuleInfo ref : findModuleInfo(d, r.size() == 1 ? r.iterator().next() : null)) {
  917. if (groupName.equals (ref.group)) {
  918. continue;
  919. }
  920. if (first) {
  921. w.print ("GROUP ");
  922. w.print (groupName);
  923. w.println ();
  924. first = false;
  925. }
  926. w.print (print);
  927. w.print (ref.getName (false));
  928. w.println ();
  929. }
  930. }
  931. }
  932. w.close ();
  933. }
  934. /** For a given dependency finds the module(s) that this dependency refers to.
  935. */
  936. private Set<ModuleInfo> findModuleInfo(Dependency dep, ModuleInfo referrer) throws BuildException {
  937. if (dep.isSpecial()) {
  938. return Collections.emptySet();
  939. }
  940. Set<ModuleInfo> result = new LinkedHashSet<ModuleInfo>();
  941. for (ModuleInfo info : modules) {
  942. if (dep.isDependingOn (info)) {
  943. result.add(info);
  944. }
  945. }
  946. if (dep.type != Dependency.Type.RECOMMENDS && result.isEmpty()) {
  947. throw new BuildException ("Cannot find module that satisfies dependency: " + dep + (referrer != null ? " from: " + referrer : ""));
  948. }
  949. return result;
  950. }
  951. /** For a given codebasename finds module that we depend on
  952. */
  953. private ModuleInfo findModuleInfo (String cnb) throws BuildException {
  954. for (ModuleInfo info : modules) {
  955. if (info.codebasename.equals(cnb)) {
  956. return info;
  957. }
  958. }
  959. return null;
  960. }
  961. private static void addDependencies (TreeSet<Dependency> addTo, java.util.jar.Manifest man, Dependency.Type dependencyType, String attrName) throws BuildException {
  962. String value = man.getMainAttributes ().getValue (attrName);
  963. if (value == null) {
  964. return;
  965. }
  966. StringTokenizer tok = new StringTokenizer (value, ",");
  967. while (tok.hasMoreElements ()) {
  968. String nextDep = tok.nextToken ();
  969. StringTokenizer dep = new StringTokenizer (nextDep, "=>", true);
  970. if (dep.countTokens () == 1) {
  971. addTo.add (new Dependency (dep.nextToken ().trim (), dependencyType, false, null));
  972. continue;
  973. }
  974. if (dep.countTokens () == 3) {
  975. String name = dep.nextToken ().trim ();
  976. String equal = dep.nextToken ().trim ();
  977. String comp = dep.nextToken ().trim ();
  978. addTo.add (new Dependency (name, dependencyType, equal.equals ("="), comp));
  979. continue;
  980. }
  981. throw new BuildException ("Cannot parse dependency: " + value);
  982. }
  983. }
  984. public static final class Input extends Object {
  985. public FileSet jars;
  986. public String name;
  987. public FileSet createJars() {
  988. if (jars != null) throw new BuildException ();
  989. jars = new FileSet();
  990. return jars;
  991. }
  992. public void setName (String name) {
  993. this.name = name;
  994. }
  995. }
  996. public static class InputPattern {
  997. private File dir;
  998. public void setDir(File dir) {
  999. this.dir = dir;
  1000. }
  1001. Collection<Input> inputs() {
  1002. List<Input> inputs = new ArrayList<Input>();
  1003. for (File cluster : dir.listFiles()) {
  1004. if (!new File(cluster, "update_tracking").isDirectory()) {
  1005. continue;
  1006. }
  1007. Input i = new Input();
  1008. i.name = cluster.getName().replaceFirst("[0-9.]+$", "");
  1009. i.jars = new FileSet();
  1010. i.jars.setDir(cluster);
  1011. i.jars.createInclude().setName("modules/*.jar");
  1012. i.jars.createInclude().setName("lib/*.jar");
  1013. i.jars.createInclude().setName("core/*.jar");
  1014. inputs.add(i);
  1015. }
  1016. return inputs;
  1017. }
  1018. }
  1019. public static final class Output extends Object {
  1020. public OutputType type;
  1021. public File file;
  1022. public void setType (OutputType type) {
  1023. this.type = type;
  1024. }
  1025. public void setFile (File file) {
  1026. this.file = file;
  1027. }
  1028. }
  1029. public static final class OutputType extends EnumeratedAttribute {
  1030. public String[] getValues () {
  1031. return new String[] {
  1032. "public-packages",
  1033. "friend-packages",
  1034. "shared-packages",
  1035. "modules",
  1036. "disabled-autoloads",
  1037. "dependencies",
  1038. "implementation-dependencies",
  1039. "group-dependencies",
  1040. "group-implementation-dependencies",
  1041. "group-friend-packages",
  1042. "external-libraries",
  1043. "kits",
  1044. "kit-dependencies",
  1045. "plugins",
  1046. "reverse-dependencies",
  1047. };
  1048. }
  1049. }
  1050. private static final class ModuleInfo extends Object implements Comparable<ModuleInfo> {
  1051. public final String group;
  1052. public final File file;
  1053. public final String codebasename;
  1054. public String publicPackages;
  1055. public Set<String> friends;
  1056. public int majorVersion;
  1057. public String specificationVersion;
  1058. public String implementationVersion;
  1059. public Set<Dependency> depends;
  1060. public Set<String> provides;
  1061. public boolean showInAutoupdate;
  1062. public boolean isEssential;
  1063. public boolean isAutoload;
  1064. public boolean isEager;
  1065. public String displayName;
  1066. public String displayCategory;
  1067. public ModuleInfo (String g, File f, String a) {
  1068. this.group = g;
  1069. this.file = f;
  1070. this.codebasename = a;
  1071. }
  1072. public int compareTo(ModuleInfo m) {
  1073. return codebasename.compareTo (m.codebasename);
  1074. }
  1075. public @Override boolean equals(Object obj) {
  1076. if (obj instanceof ModuleInfo) {
  1077. return codebasename.equals(((ModuleInfo) obj).codebasename);
  1078. }
  1079. return false;
  1080. }
  1081. public @Override int hashCode() {
  1082. return codebasename.hashCode ();
  1083. }
  1084. public String getName(boolean includeMajorVersion) {
  1085. if (!includeMajorVersion || majorVersion == -1) {
  1086. return codebasename + " (" + group + ")";
  1087. } else {
  1088. return codebasename + "/" + majorVersion + " (" + group + ")";
  1089. }
  1090. }
  1091. public @Override String toString() {
  1092. return "ModuleInfo[" + getName (false) + "]";
  1093. }
  1094. } // end of ModuleInfo
  1095. private static final class Dependency extends Object implements Comparable<Dependency> {
  1096. enum Type {DIRECT, REQUIRES, RECOMMENDS}
  1097. public final String token;
  1098. public final int majorVersionFrom;
  1099. public final int majorVersionTo;
  1100. public final Type type;
  1101. public final boolean exact;
  1102. public final String compare;
  1103. public Dependency (String token, Type type, boolean exact, String compare) {
  1104. // base name
  1105. int slash = token.indexOf ('/');
  1106. if (slash == -1) {
  1107. this.token = token;
  1108. this.m