001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.File;
045: import java.io.FileInputStream;
046: import java.io.IOException;
047: import java.io.InputStream;
048: import java.io.InputStreamReader;
049: import java.util.SortedMap;
050: import java.util.TreeMap;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053: import java.util.regex.Matcher;
054: import java.util.regex.Pattern;
055: import org.apache.tools.ant.BuildException;
056: import org.apache.tools.ant.Project;
057: import org.apache.tools.ant.taskdefs.Jar;
058: import org.apache.tools.ant.taskdefs.Manifest;
059:
060: /**
061: * Task just like <jar> but predefines various module attributes.
062: * Cf. projectized.xml#jar
063: * @author Jesse Glick
064: */
065: public class JarWithModuleAttributes extends Jar {
066:
067: public JarWithModuleAttributes() {
068: }
069:
070: private static final Pattern COMMA_SPACE = Pattern.compile(", *");
071: private static final Pattern IMPL_DEP = Pattern
072: .compile(" *([a-zA-Z0-9_.]+)(/[0-9]+)? *= *(.+) *");
073:
074: private File stamp;
075:
076: /** Location of a stamp file to create and/or make newer than the JAR file.
077: *
078: * @param stamp the file to create and update
079: */
080: public void setStamp(File stamp) {
081: this .stamp = stamp;
082: }
083:
084: @Override
085: public void setManifest(File manifestFile) throws BuildException {
086: Manifest added = new Manifest();
087: try {
088: String pubPkgs = getProject()
089: .getProperty("public.packages");
090: if (pubPkgs == null) {
091: throw new BuildException(
092: "Must have defined 'public.packages'",
093: getLocation());
094: }
095: added.addConfiguredAttribute(new Manifest.Attribute(
096: "OpenIDE-Module-Public-Packages", pubPkgs));
097: String friends = getProject().getProperty("friends");
098: if (friends != null) {
099: added.addConfiguredAttribute(new Manifest.Attribute(
100: "OpenIDE-Module-Friends", friends));
101: }
102: // #52354: define Class-Path in the manifest automatically.
103: String javahelpClassPathExtension = getProject()
104: .getProperty("javahelp.class.path.extension");
105: String classPathExtensions = getProject().getProperty(
106: "class.path.extensions");
107: String cp;
108: if (javahelpClassPathExtension != null) {
109: if (classPathExtensions != null) {
110: cp = classPathExtensions + " "
111: + javahelpClassPathExtension;
112: } else {
113: cp = javahelpClassPathExtension;
114: }
115: } else {
116: if (classPathExtensions != null) {
117: cp = classPathExtensions;
118: } else {
119: cp = null;
120: }
121: }
122: if (cp != null) {
123: added.addConfiguredAttribute(new Manifest.Attribute(
124: "Class-Path", cp));
125: }
126: String moduleDeps = getProject().getProperty(
127: "module.dependencies");
128: if (moduleDeps != null) {
129: added.addConfiguredAttribute(new Manifest.Attribute(
130: "OpenIDE-Module-Module-Dependencies",
131: moduleDeps));
132: }
133: String javaDep = getProject().getProperty("javac.target");
134: if (javaDep != null
135: && javaDep.matches("[0-9]+(\\.[0-9]+)*")) {
136: added.addConfiguredAttribute(new Manifest.Attribute(
137: "OpenIDE-Module-Java-Dependencies", "Java > "
138: + javaDep));
139: }
140: // Check to see if OpenIDE-Module-Implementation-Version is already defined.
141: String implVers;
142: String ownCnb;
143: Manifest staticManifest;
144: InputStream is = new FileInputStream(manifestFile);
145: try {
146: staticManifest = new Manifest(new InputStreamReader(is,
147: "UTF-8"));
148: Manifest.Section mainSection = staticManifest
149: .getMainSection();
150: implVers = mainSection
151: .getAttributeValue("OpenIDE-Module-Implementation-Version");
152: String myself = mainSection
153: .getAttributeValue("OpenIDE-Module");
154: int slash = myself.indexOf('/');
155: if (slash == -1) {
156: ownCnb = myself;
157: } else {
158: ownCnb = myself.substring(0, slash);
159: }
160: String cnbs = getProject().getProperty(
161: "code.name.base.slashes");
162: String cnbDots = (cnbs != null) ? cnbs
163: .replace('/', '.') : null;
164: if (!ownCnb.equals(cnbDots)) {
165: // #58248: make sure these stay in synch.
166: throw new BuildException(
167: "Mismatch in module code name base: manifest says "
168: + ownCnb + " but project.xml says "
169: + cnbDots, getLocation());
170: }
171: } finally {
172: is.close();
173: }
174: String buildNumber = getProject()
175: .getProperty("buildnumber");
176: if (buildNumber == null) {
177: throw new BuildException(
178: "Must have defined 'buildnumber'",
179: getLocation());
180: }
181: String attrToAdd = implVers != null ? "OpenIDE-Module-Build-Version"
182: : "OpenIDE-Module-Implementation-Version";
183: added.addConfiguredAttribute(new Manifest.Attribute(
184: attrToAdd, buildNumber));
185: // If spec.version.base is defined, use it, after tacking on any numeric impl deps (sorted by CNB of the dep for stability),
186: // and also using the implementation version of this module as well if it is numeric.
187: // This trick makes sure that if you have an impl dep on some module which changes its (numeric) impl version,
188: // your spec version will also change correspondingly, so e.g. Auto Update will see a new version of your module too.
189: String specVersBase = getProject().getProperty(
190: "spec.version.base");
191: if (specVersBase != null) {
192: boolean edited = false;
193: if (implVers != null) {
194: try {
195: Integer implVersI = new Integer(implVers);
196: specVersBase += "." + implVersI;
197: edited = true;
198: } catch (NumberFormatException e) {
199: // OK, ignore it, not numeric.
200: getProject()
201: .log(
202: manifestFile
203: + ": warning: use of spec.version.base with non-integer OpenIDE-Module-Implementation-Version (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
204: Project.MSG_WARN);
205: }
206: }
207: SortedMap<String, Integer> additions = new TreeMap<String, Integer>();
208: if (moduleDeps != null) {
209: for (String individualDep : COMMA_SPACE
210: .split(moduleDeps)) {
211: Matcher m = IMPL_DEP.matcher(individualDep);
212: if (m.matches()) {
213: String cnb = m.group(1);
214: String version = m.group(3);
215: try {
216: if (version.length() > 1
217: && version.charAt(0) == '0') {
218: // Could be interpreted as an integer, but not here - e.g. "050123" is a date.
219: throw new NumberFormatException(
220: version);
221: }
222: Integer versionI = new Integer(version);
223: additions.put(cnb, versionI);
224: } catch (NumberFormatException e) {
225: // OK, ignore this one, not numeric.
226: getProject()
227: .log(
228: "Warning: in "
229: + ownCnb
230: + ", use of spec.version.base with non-integer OpenIDE-Module-Implementation-Version from "
231: + cnb
232: + " (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
233: Project.MSG_WARN);
234: }
235: }
236: }
237: }
238: for (int version : additions.values()) {
239: specVersBase += "." + version;
240: edited = true;
241: }
242: if (!edited) {
243: getProject()
244: .log(
245: "Warning: in "
246: + ownCnb
247: + ", using spec.version.base for no reason; could just use OpenIDE-Module-Specification-Version statically in the manifest (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
248: Project.MSG_WARN);
249: }
250: if (staticManifest.getMainSection().getAttributeValue(
251: "OpenIDE-Module-Specification-Version") != null) {
252: getProject()
253: .log(
254: "Warning: in "
255: + ownCnb
256: + ", attempting to use spec.version.base while some OpenIDE-Module-Specification-Version is statically defined in manifest.mf; this cannot work (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
257: Project.MSG_WARN);
258: } else {
259: added
260: .addConfiguredAttribute(new Manifest.Attribute(
261: "OpenIDE-Module-Specification-Version",
262: specVersBase));
263: }
264: } else if (moduleDeps != null
265: && moduleDeps.indexOf('=') != -1) {
266: getProject()
267: .log(
268: "Warning: in "
269: + ownCnb
270: + ", not using spec.version.base, yet declaring implementation dependencies; may lead to problems with Auto Update (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
271: Project.MSG_WARN);
272: } else if (implVers != null) {
273: try {
274: new Integer(implVers);
275: } catch (NumberFormatException e) {
276: getProject()
277: .log(
278: manifestFile
279: + ": warning: use of non-integer OpenIDE-Module-Implementation-Version may be problematic for clients trying to use spec.version.base (see http://wiki.netbeans.org/wiki/view/DevFaqImplementationDependency)",
280: Project.MSG_WARN);
281: }
282: }
283: boolean old = false; // #110661
284: String destDir = getProject().getProperty(
285: "netbeans.dest.dir");
286: if (destDir != null) {
287: for (File cluster : getProject().resolveFile(destDir)
288: .listFiles()) {
289: if (new File(cluster,
290: "modules/org-netbeans-modules-autoupdate.jar")
291: .isFile()) {
292: old = true;
293: break;
294: }
295: }
296: }
297: if (!old) {
298: added
299: .addConfiguredAttribute(new Manifest.Attribute(
300: "AutoUpdate-Show-In-Client",
301: Boolean.toString( // #110572
302: !Project.toBoolean(getProject()
303: .getProperty(
304: "is.autoload"))
305: && !Project
306: .toBoolean(getProject()
307: .getProperty(
308: "is.eager"))
309: && "modules"
310: .equals(getProject()
311: .getProperty(
312: "module.jar.dir")))));
313: }
314: // Now ask the regular <jar> task to add all this stuff to the regular manifest.mf.
315: added.merge(staticManifest);
316: if (!"lib".equals(getProject()
317: .getProperty("module.jar.dir"))) {
318: // modules in lib cannot request this token
319: String key = "OpenIDE-Module-Requires";
320: String token = "org.openide.modules.ModuleFormat1";
321: String requires = staticManifest.getMainSection()
322: .getAttributeValue(key);
323: String newRequires;
324: if (requires != null) {
325: // #59671: have to modify it, not just use super.setManifest(manifestFile).
326: added.getMainSection().removeAttribute(key);
327: newRequires = requires + ", " + token;
328: } else {
329: newRequires = token;
330: }
331: added.addConfiguredAttribute(new Manifest.Attribute(
332: key, newRequires));
333: }
334: addConfiguredManifest(added);
335: } catch (Exception e) {
336: throw new BuildException(e, getLocation());
337: }
338: }
339:
340: @Override
341: public void execute() throws BuildException {
342: super .execute();
343: if (stamp != null) {
344: log("Stamp " + stamp + " against " + zipFile,
345: Project.MSG_DEBUG);
346: if (stamp.lastModified() < zipFile.lastModified()) {
347: try {
348: stamp.getParentFile().mkdirs();
349: stamp.createNewFile();
350: stamp.setLastModified(zipFile.lastModified());
351: } catch (IOException ex) {
352: throw new BuildException(ex);
353: }
354: }
355: }
356: }
357:
358: }
|