diff options
Diffstat (limited to 'build')
48 files changed, 8229 insertions, 0 deletions
diff --git a/build/installer-resources/aspectjBanner.gif b/build/installer-resources/aspectjBanner.gif Binary files differnew file mode 100644 index 000000000..d33e0a733 --- /dev/null +++ b/build/installer-resources/aspectjBanner.gif diff --git a/build/installer-resources/contents.txt b/build/installer-resources/contents.txt new file mode 100644 index 000000000..275d2b4ff --- /dev/null +++ b/build/installer-resources/contents.txt @@ -0,0 +1,3 @@ +2000 +org/aspectj/tools/ajc/Main.class + diff --git a/build/installer-resources/properties.txt b/build/installer-resources/properties.txt new file mode 100644 index 000000000..f2ee1b595 --- /dev/null +++ b/build/installer-resources/properties.txt @@ -0,0 +1,7 @@ +installer.main.class: @installer.main.class@ +installer.basedir: aspectj@build.version.majorminor@ +build.version.long: @build.version.long@ +build.version.short: @build.version.short@ +build.date: @build.date@ +build.time: @build.time@ +copyright.allRights.from1998: @copyright.allRights.from1998@ diff --git a/build/lib/BridgeVersion.java.txt b/build/lib/BridgeVersion.java.txt new file mode 100644 index 000000000..95d41d201 --- /dev/null +++ b/build/lib/BridgeVersion.java.txt @@ -0,0 +1,66 @@ +/* ******************************************************************** + * Copyright (c) 1998-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * *******************************************************************/ + +package org.aspectj.bridge; + +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** release-specific version information */ +public class Version { + + /** default version value for development version */ + public static final String DEVELOPMENT = "DEVELOPMENT"; + + /** default time value for development version */ + public static final long NOTIME = 0L; + + /** set by build script */ + public static final String text = "@build.version@"; + + /** + * Time text set by build script using SIMPLE_DATE_FORMAT. + * (if DEVELOPMENT version, invalid) + */ + public static final String time_text = "@build.time@"; + + /** + * time in seconds-since-... format, used by programmatic clients. + * (if DEVELOPMENT version, NOTIME) + */ + public static final long time; + + /** format used by build script to set time_text */ + public static final String SIMPLE_DATE_FORMAT = "@build.time.format@"; + + // if not DEVELOPMENT version, read time text using format used to set time + static { + long foundTime = NOTIME; + if (!DEVELOPMENT.equals(text)) { + try { + SimpleDateFormat format = new SimpleDateFormat(SIMPLE_DATE_FORMAT); + ParsePosition pos = new ParsePosition(0); + Date date = format.parse(time_text, pos); + foundTime = date.getTime(); + } catch (Throwable t) { + } + } + time = foundTime; + } +} + + + + + diff --git a/build/products/aspectj/install/configure-auto.html b/build/products/aspectj/install/configure-auto.html new file mode 100644 index 000000000..7abdaac6b --- /dev/null +++ b/build/products/aspectj/install/configure-auto.html @@ -0,0 +1,17 @@ +<html> + +<head> +<title>auto configure</title> +</head> + +<body> + +<p>The installer has successfully found the path to your Java home (J2SE 1.3 or greater). +This path will be used as the default Java location when generating +script files for launching the AspectJ compiler and core tools. Unless +you know that this path is wrong, we suggest that you press +<b>Next</b> to continue.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/configure-hand.html b/build/products/aspectj/install/configure-hand.html new file mode 100644 index 000000000..73b3e2185 --- /dev/null +++ b/build/products/aspectj/install/configure-hand.html @@ -0,0 +1,21 @@ +<html> + +<head> +<title>configure</title> +</head> + +<body> + +<p>The installer cannot find the path to your Java home (J2SE 1.3 or greater). +Please try to find this directory on your path now. It will probably look +something like <code>jdk1.3.1</code>. If you can't find this +directory now, you may continue with the install, but you will have +to either edit your launch scripts by hand or to set the JAVA_HOME +environment variable to point to the right location. +</p> + +<p>Press <b>Next</b> when ready to continue.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/finish.html b/build/products/aspectj/install/finish.html new file mode 100644 index 000000000..204deb550 --- /dev/null +++ b/build/products/aspectj/install/finish.html @@ -0,0 +1,28 @@ +<html> + +<head> +<title>finish</title> +</head> + +<body> + +<p>The automatic installation process is complete. +We recommend you complete the installation as follows:</p> +<ol> + <li>Add <b><code>${installer.output.aspectjrt}</code></b> + to your CLASSPATH. + This small .jar file contains classes required by any + program compiled with the ajc compiler. + </li> + <li>Modify your PATH to include + <code> <b>${installer.output.dir.bin}</b></code>. + This will make it easier to run ajc and ajbrowser. + </li> +</ol> + +<p>These steps are described in more detail in <code> +<b>${installer.output.readme}</b></code>.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/install-finish.html b/build/products/aspectj/install/install-finish.html new file mode 100644 index 000000000..14281a0bf --- /dev/null +++ b/build/products/aspectj/install/install-finish.html @@ -0,0 +1,12 @@ +<html> + +<body> + +<p> </p> + +<p>The automatic installation process is complete. Press <b>Next </b>for +some important final instructions.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/install-start.html b/build/products/aspectj/install/install-start.html new file mode 100644 index 000000000..b375c68aa --- /dev/null +++ b/build/products/aspectj/install/install-start.html @@ -0,0 +1,13 @@ +<html> + +<body> + +<p> </p> + +<p>Now installing to ${installer.output.dir}...</p> + +<p>Press <b>Cancel</b> to interrupt the installation.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/intro.html b/build/products/aspectj/install/intro.html new file mode 100644 index 000000000..58e1ee4df --- /dev/null +++ b/build/products/aspectj/install/intro.html @@ -0,0 +1,25 @@ +<html> +<body> + +<h2 align="center">Installer for AspectJ<sup><small>TM</small></sup></h2> + +<p align="center">Version ${build.version.long} built on ${build.date}</p> +<p>This installs the complete AspectJ distribution, with +the compiler, structure browser, ant tasks, +documentation, and examples. +This distribution is covered by the Common Public License (see + http://www.eclipse.org/legal/cpl-v10.html). +<p> +For IDE integrations or source code, see the project home page at +http://eclipse.org/aspectj</p> + + +<p>${copyright.allRights.from1998}</p> +<p></p> + +<p>Press <b>Next</b> to continue. At any time you may press <b>Cancel</b> +to exit the installation process.</p> + +</body> + +</html> diff --git a/build/products/aspectj/install/location.html b/build/products/aspectj/install/location.html new file mode 100644 index 000000000..0398ce4a5 --- /dev/null +++ b/build/products/aspectj/install/location.html @@ -0,0 +1,11 @@ +<html> + +<body> + +<p>Please select a directory into which to install AspectJ.</p> +<p>Press <b>Install</b> to begin the installation process + to this directory.</p> + +</body> + +</html> diff --git a/build/products/tools/dist/LICENSE-TOOLS.html b/build/products/tools/dist/LICENSE-TOOLS.html new file mode 100644 index 000000000..27bbede73 --- /dev/null +++ b/build/products/tools/dist/LICENSE-TOOLS.html @@ -0,0 +1,151 @@ +<html> + +<head> +<title>AspectJ License</title> +</head> + +<BODY BGCOLOR="white"> + +<h2 align="center"><b>AspectJ<sup>TM</sup> Compiler and Core Tools License</b></h2> + +<p>This is a binary-only release. Source code +is available from +<a href="@aspectj.home.url@">@aspectj.home.url@</a></p> + +<p>The AspectJ compiler and core tools are distributed under the +Common Public License version 1.0 available + <a href="http://eclipse.org/legal/cpl-v10.html">here</a> + and copied below. + This license has been approved by +the <a href="http://www.opensource.org">Open Source Initiative</a> as +conforming to the <a href="http://www.opensource.org/osd.html">Open +Source Definition</a>. +More information about the history and rationale behind this license +can be found at the + <a href="http://eclipse.org">eclipse web site</a>.</p> + +<p> +Those portions of this distribution in the <code>org.apache</code> +Java namespace are available under the terms of +the Apache Software License, Version 1.1 +(See <a href="http://jakarta.apache.org"> + http://jakarta.apache.org</a>). +</p> +<hr> + +<P ALIGN="CENTER"><B>Common Public License - v 1.0</B> +<P><B></B><FONT SIZE="3"></FONT> +<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>1. DEFINITIONS</B></FONT> +<P><FONT SIZE="2">"Contribution" means:</FONT> + +<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT"> +b) in the case of each subsequent Contributor:</FONT></UL> + + +<UL><FONT SIZE="2">i) changes to the Program, and</FONT></UL> + + +<UL><FONT SIZE="2">ii) additions to the Program;</FONT></UL> + + +<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. </FONT><FONT SIZE="2">Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. </FONT></UL> + +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. </FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT> +<P><FONT SIZE="2"><B></B></FONT> +<P><FONT SIZE="2"><B>2. GRANT OF RIGHTS</B></FONT> + +<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a) </FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. </FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2">c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + + +<UL><FONT SIZE="2">d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL> + + +<UL><FONT SIZE="2"></FONT></UL> + +<P><FONT SIZE="2"><B>3. REQUIREMENTS</B></FONT> +<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT> + +<UL><FONT SIZE="2">a) it complies with the terms and conditions of this Agreement; and</FONT></UL> + + +<UL><FONT SIZE="2">b) its license agreement:</FONT></UL> + + +<UL><FONT SIZE="2">i) effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL> + + +<UL><FONT SIZE="2">ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL> + + +<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2"> states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL> + + +<UL><FONT SIZE="2">iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL> + + +<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL> + +<P><FONT SIZE="2">When the Program is made available in source code form:</FONT> + +<UL><FONT SIZE="2">a) it must be made available under this Agreement; and </FONT></UL> + + +<UL><FONT SIZE="2">b) a copy of this Agreement must be included with each copy of the Program. </FONT></UL> + +<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT> +<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program. </FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. </FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>4. COMMERCIAL DISTRIBUTION</B></FONT> +<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT> +<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5. NO WARRANTY</B></FONT> +<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED 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. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6. DISCLAIMER OF LIABILITY</B></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"><B>7. GENERAL</B></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. </FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version. </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2"> All rights in the Program not expressly granted under this Agreement are reserved.</FONT> +<P><FONT SIZE="2"></FONT> +<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.</FONT> +<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT> +<P><FONT SIZE="2"></FONT> + +<hr> +</body> + +</html> diff --git a/build/products/tools/dist/README-11.html b/build/products/tools/dist/README-11.html new file mode 100644 index 000000000..b58a650da --- /dev/null +++ b/build/products/tools/dist/README-11.html @@ -0,0 +1,1067 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> <head> +<title>AspectJ 1.1beta1 -- Readme</title> +<style type="text/css"> +<!-- + P { margin-left: 20px; } + PRE { margin-left: 20px; } + LI { margin-left: 20px; } + H4 { margin-left: 20px; } + H3 { margin-left: 10px; } +--> +</style> +</head> + +<body> +<p align="right"><small> +© Copyright 2002 Palo Alto Research Center, Incorporated. +All rights reserved. +</small></p> + +<h1>AspectJ Readme</h1> + +<p align="right"><i>Version 1.1beta1</i></p> + +<p> This is an early-access release of AspectJ 1.1. It includes a small +number of new language features as well as major improvements to the +functionality of the tools. </p> + +<p> This document is intended to completely describe the 1.1 language +and tools in relation to AspectJ 1.0.6. With that in mind, many +features are documented sketchily with the flag "not working in +1.1beta1", but we believe it to include some description of all the +features of 1.1 that are different from those in 1.0.6. +</p> + +<p>The full documentation package for the 1.1 release is not yet +ready, so this document goes into a bit more detail than usual changes +documents. All descriptions here should be read as changes to the +<a href="http://aspectj.org/documentation/dist">The AspectJ 1.0.6 +documentation</a>. </p> + +<p> The changes from the 1.1alpha release are: +</p> + + +<ul> + <li>More 1.0 features have been implemented: <code>perthis</code>, + <code>pertarget</code>, <code>percflow</code>, + <code>withincode</code>, <code>if</code>, <code>cflow</code>, + <code>cflowbelow</code>, <code>privileged aspects</code>, and + <code>declare soft</code>. </li> + + <li>Two new compiler flags, <a href="#NO_WEAVE">-noweave</a> and + <a href="#BINARY_ASPECTS">-aspectpath</a>, are supported.</li> + + <li>The <a href="#XLINT">-Xlint</a> option is now partially supported.</li> + + <li>Source information is better maintained.</li> + + <li><code>declare soft</code> and inter-type field declarations have + additional <a href="#NEW_LIMITATIONS">restrictions</a> that were not + present in 1.0. </li> + + <li>The status of <a href="#tools">ajdoc</a> is under consideration.</li> + + <li>Eclipse 2.0 will be supported by an upcoming AJDT release</li> + + <li>Netbeans 3.4 is supported, and compiler message output is better + integrated.</li> + + <li>Method executions now show up in the tools' structure model.</li> +</ul> + +<p> This document begins with short descriptions of changes from the +1.0 release. +</p> + +<ul> + <li><a href="#language">the language</a>,</li> + <li><a href="#compiler">the compiler</a>,</li> + <li><a href="#tools">the support tools</a>,</li> + <li><a href="#runtime">the runtime</a>,</li> + <li><a href="#devenv">the development environment support</a>, and</li> + <li><a href="#sources">the sources</a>,</li> +</ul> + +<p> followed by <a href="#details">details</a> of many of the changes. +</p> + +<!-- ============================== --> +<hr> +<h2><a name="language">The Language</a></h2> + + <p> AspectJ 1.1 is a slightly different language than AspectJ 1.0. + In all but a few cases, programs written in AspectJ 1.0 should + compile correctly in AspectJ 1.1. In many cases, there are + new or preferred forms in AspectJ 1.1. However, some AspectJ 1.0 + features are not yet implemented in 1.1, so many 1.0 programs + will not compile in this release. + </p> + + <p> + Most changes to the language are additions to expressibility + requested by our users: + </p> + + <ul> + <li><a href="#THROWS_PATTERN">Matching based on throws</a>: You can + now make finer discriminations between methods based on declared + exceptions. </li> + + <li><a href="#NEW_PCDS">New kinded pointcut designators</a>: Now + every kind of pointcut has a corresponding kinded pointcut + designator. </li> + + <li><a href="#PER_TYPE">New pertype aspect specifier</a>: This + is a potential feature whose status is still uncertain. </li> + </ul> + + <p> Some are have different behavior in edge cases but offer + improved power and clarity: </p> + + <ul> + <li><a href="#ASPECT_PRECEDENCE">New aspect precedence form</a>: + AspectJ 1.1 has a new declare form, declare dominates, that is + intended to replace the "dominates" clause on aspects. </li> + </ul> + + <p> But in order to support weaving into bytecode effectively, + several incompatible changes had to be made to the language: </p> + + <ul> + <li><a href="#CALLEE_SIDE_CALLS">Different callee-side call join + points</a></li> + + <li><a href="#SINGLE_INTERCLASS_TARGET">One target for intertype + declarations</a></li> + + <li><a href="#UNAVAILABLE_JOIN_POINTS">No initializer execution join + points</a></li> + + <li><a href="#CONSTRUCTOR_EXECUTION_IS_BIGGER">Initializers run + inside constructor execution join points</a></li> + + <li><a href="#WITHIN_MEMBER_TYPES">Small limitations of the within + pointcut</a></li> + + <li><a href="#WITHIN_CODE">Small limitations of the withincode + pointcut</a></li> + + <li><a href="#INSTANCEOF_ON_WILD">Can't do instanceof matching on + type patterns with wildcards</a></li> + + <li><a href="#NO_SOURCE_COLUMN">SourceLocation.getColumn() is + deprecated and will always return 0</a></li> + + </ul> + + <p><a name="NEW_LIMITATIONS">There</a> are a couple of language + limitations for things that are rarely used that make the + implementation simpler. If no one speaks up in defense of these, + they will probably become official simplifications of the language. + If defenders speak up, then they will be listed as limitations of + the current compiler implementation. </p> + + <ul> + <li>'declare soft: TYPE: POINTCUT;' In 1.0 a TYPE_PATTERN was + allowed rather than a TYPE. This limitation makes declare soft + much easier to implement efficiently.</li> + + <li>Inter-type field declarations only allow a single field per + line, i.e. this is now illegal 'int C.field1, D.field2;' This must + instead be, 'int C.field1; int D.field2;'</li> + </ul> + + <p>Finally, there are several AspectJ-1.0 language features that are + unimplemented in 1.1beta1 because we didn't have the time. These + features should all be available in 1.1rc1.</p> + + <ul> + <li><a href="#MISSING_LANGUAGE">Language features unimplemented in + 1.1beta1</a>: initialization, preinitialization and handler + pcds/join points; args pcd with multiple ..'s; inter-type + constructor declarations. </li> + </ul> + +<!-- ============================== --> +<hr> +<h2><a name="compiler">The Compiler</a></h2> + + <p> The compiler for AspectJ 1.1 is different than the compiler for + AspectJ 1.0. While this document describes the differences in the + compiler, it's worthwhile noting that much effort has been made to + make sure that the interface to ajc 1.1 is, as much as possible, the + same as the interface to ajc 1.0. There are two important changes + under the hood, however. </p> + + <p> First, The 1.1 compiler is implemented on top of IBM's + open-source Eclipse compiler. This has two benefits: It allows us + to concentrate on the AspectJ extensions to Java and let the Eclipse + team worry about making sure the Java edge cases work, and it allows + us to piggyback on Eclipse's already mature incremental compilation + facilities. </p> + + <p> Second, ajc now cleanly deliniates compilation of source code + from assembly (or "weaving") of bytecode. The compiler still + accepts source code, but internally it transforms it into bytecode + format before weaving. </p> + + <p> This new architecture, and other changes to the compiler, allows + us to implement some features that were defined in the AspectJ 1.0 + language but not implementable in the 1.1 compiler (after returning + advice on handler join points will probably be one of those + features, when handler join points are finished). It also makes + some new features available: </p> + + <ul> + <li><a href="#SOURCEROOT">The -sourceroots option</a>: This option + takes one or more directories, and indicates that all the source + files in those directories should be passed to the compiler. </li> + + <li><a href="#BYTECODE_WEAVING">The -injars option</a>: This option + takes one or more jar files, and indicates that all the classfiles + in the jar files should be woven into. </li> + + <li><a href="#NO_WEAVE">-noweave, a compiler option to suppress + weaving</a></li> + + <li><a href="#BINARY_ASPECTS">-aspectpath, working with aspects in + .class/.jar form</a></li> + + <li><a href="#OUTJAR">The -outjar option</a>: This option indicates + that the result classfiles of compiling and weaving should be placed + in the specified jar file. </li> + + <li><a href="#XLINT">The -Xlint option is partially implemented in + 1.1beta1.</a></li> + + <li><a href="#INCREMENTAL">Incremental compilation</a>: The AspectJ + 1.1 compiler supports incremental compilation. </li> + </ul> + + <p> Some other features we plan to support for 1.1, but did not make + it into this beta release: </p> + + <ul> + <li><a href="#NO_AROUND_OPTIMIZATION">Inlining around advice</a>: The + implementation of around advice is not optimized at all in the + beta, and so will result in slower generated code than that + generated by ajc 1.0. </a></li> + + <li><a href="#NO_CALLEE_SIDE_CALL">Callee-side call join points</a>: + The beta compiler does not expose call join points unless it is + given the calling code. </li> + + <li><a href="#EXCEPTION_CHECKING">Exception checking is not + handled completely during weaving</a></li> + + <li><a href="#ERROR_MESSAGES">Error messages will sometimes be scary</a></li> + + <li><a href="#OTHER_X_OPTIONS">Various -X options</a></li> + + <li><a href="#MISSING_LANGUAGE">Several language features are unimplemented in + 1.1beta1</a></li> + </ul> + + <p> But some features of the 1.0 compiler are not supported in the + 1.1 compiler: </p> + + <ul> + <li><a href="#NO_SOURCE">Source-specific options</a>: The -source, + -usejavac, -nocomment and -workingdir options are no longer + supported</li> + + <li><a href="#NO_STRICT_LENIENT">The -strict and -lenient options</a> + </li> + + <li><a href="#NO_PORTING">The -porting option</a></li> + + <li><a href="#13_REQUIRED">J2SE 1.2 is not supported; + J2SE 1.3 or later is required;.</a></li> + </ul> + + <p> A short description of the options ajc accepts is available with + "ajc -help". </p> + <p> </p> + +<!-- ============================== --> +<hr> +<h2><a name="tools">Support Tools</a></h2> + + + <p>This release includes two new experimental Ant variants, + a CompilerAdapter to support running <tt>ajc</tt> with the Javac task + by setting the build.compiler property and a task that supports + incremental mode by watching a file. + </p> + + <p>This release does not include <tt>ajdoc</tt>, the documentation tool for + AspectJ sources, and its 1.1 release will trail the others' by weeks. + <tt>Ajdoc</tt> is deeply dependent on the abstract syntax tree + classes from the old compiler, so it needs a bottom-up rewrite. + We think it best to use this opportunity to implement more general + API's for publishing and rendering static structure. Because those API's + are last to settle in the new architecture, and because the compiler itself + is a higher priority, we are delaying work on <tt>ajdoc</tt> until + after the 1.1 release.</p> + + <p>AspectJ 1.1 will not include <tt>ajdb</tt>, the AspectJ stand-alone debugger. + It is no longer necessary because more third-party debuggers are starting + to work according to JSR 45, "Debugging support for other languages", + which we plan to support in AspectJ 1.1. </p> + +<!-- ============================== --> +<hr> +<h2><a name="runtime">The Runtime Library</a></h2> + + <p>This release has minor additions to the runtime library classes. + As with any release, you should compile and run with the runtime + library that came with your compiler, and you may run with + a later version of the library without recompiling your code.</p> + + <p> In one instance, however, they behave differently this release. + Because the AspectJ 1.1 compiler does its weaving through + bytecode, column numbers of source locations are not available. + Therefore, <code>thisJoinPoint.getSourceLocation().getColumn()</code> + is deprecated and will always return 0. </p> + +<!-- ============================== --> +<hr> +<h2><a name="devenv">The AJDE Tools</a></h2> + + <p> AJDE for JBuilder, AJDE for Netbeans, and AJDE for Emacs and the + AJBrowser have not changed much. They use the batch-build mode of + the new compiler. The beta compiler does not yet produce + crosscutting structure (for anything but method executions) so there are no inline + annotations in JBuilder or Emacs support and you only see a + declaration tree in the structure views. Incremental building and + bytecode weaving are not available in the AJDE tools in the beta + release, but will be in a future release.</p> + + <p> Of note for the beta is that NetBeans 3.4 is supported, and there is now + better integration for the compiler messages output window.</p> + +<!-- ============================== --> +<hr> +<h2><a name="sources">The Sources and the Licences</a></h2> + + <p> The AspectJ tools sources are available under the + <a href="http://eclipse.org/legal/cpl-v10.html">Common Public + Licence</a> in the CVS repository + at <a href="http://eclipse.org">http://eclipse.org</a>. + </p> + + <p> More information about the sources can be found in their + respective README files. </p> + +<!-- ============================== --> +<hr> +<hr> +<h2><a name="details">Details</a></h2> + + <h3><a name="THROWS_PATTERN">Matching based on throws</a></h3> + + <p> Type patterns may now be used to pick out methods and + constructors based on their throws clauses. This allows the + following two kinds of extremely wildcarded pointcuts: </p> + + <pre> pointcut throwsMathlike(): + // each call to a method with a throws clause containing at least + // one exception exception with "Math" in its name. + call(* *(..) throws *..*Math*); + + pointcut doesNotThrowMathlike(): + // each call to a method with a throws clause containing no + // exceptions with "Math" in its name. + call(* *(..) throws !*..*Math*); + </pre> + + <p> The longwinded rules are that a method or constructor pattern + can have a "throws clause pattern". Throws clause patterns look + like: </p> + + <pre> ThrowsClausePattern: + ThrowsClausePatternItem ("," ThrowsClausePatternItem)* + + ThrowsClausePatternItem: + ["!"] TypeNamePattern + </pre> + + <p> A ThrowsClausePattern matches the ThrowsClause of any code + member signature. To match, each ThrowsClausePatternItem must + match the throws clause of the member in question. If any item + doesn't match, then the whole pattern doesn't match. This rule is + unchanged from AspectJ 1.0. </p> + + <p> If a ThrowsClausePatternItem begins with "!", then it matches + a particular throws clause if and only if <em>none</em> of the + types named in the throws clause is matched by the + TypeNamePattern. </p> + + <p> If a ThrowsClausePatternItem does not begin with "!", then it + matches a throws clause if and only if <em>any</em> of the types + named in the throws clause is matched by the TypeNamePattern.</p> + + <p> These rules are completely backwards compatible with + AspectJ 1.0. The rule for "!" matching has one potentially + surprising property, in that the two PCDs shown below will have + different matching rules. </p> + + <pre> [1] call(* *(..) throws !IOException) + [2] call(* *(..) throws (!IOException)) + + void m() throws RuntimeException, IOException {} + </pre> + + <p> [1] will NOT match the method m(), because method m's throws + clause declares that it throws IOException. [2] WILL match the + method m(), because method m's throws clause declares the it + throws some exception which does not match IOException, + i.e. RuntimeException. </p> + + <h3><a name="NEW_PCDS">New kinded pointcut designators</a></h3> + + <p> AspectJ 1.0 does not provide kinded pointcut designators for + two (rarely used) join points: preinitialization (the code that + runs before a super constructor call is made) and advice + execution. AspectJ 1.1 does not change the meaning of the join + points, but will provide two new pointcut designators to pick out + these join points, thus making join points and pointcut + designators more parallel. </p> + + <p> <code>adviceexectuion()</code> will pick out advice execution + join points. You will usually want to use <code>adviceexecution() + && within(Aspect)</code> to restrict it to only those pieces of + advice defined in a particular aspect. preinitialization join + points are not yet available in AspectJ 1.1beta1. </p> + + <h3><a name="PER_TYPE">New pertype aspect specifier</a></h3> + + <p>We're strongly considering adding a pertype aspect kind to 1.1. + This is somewhat motivated by the new <a + href="#SINGLE_INTERCLASS_TARGET">restrictions on inter-type + declarations<a>. This is also motivated by many previous request + to support a common logging idiom. Here's what pertype would look + like:</p> + + <pre> /** One instance of this aspect will be created for each class, + * interface or aspect in the com.bigboxco packages. + */ + aspect Logger pertype(com.bigboxco..*) { + /* This field holds a logger for the class. */ + Log log; + + /* This advice will run for every public execution defined by + * a type for which a Logger aspect has been created, i.e. + * any type in com.bigboxco..* + */ + before(): execution(public * *(..)) { + log.enterMethod(thisJoinPoint.getSignature().getName()); + } + + /* We can use a special constructor to initialize the log field */ + public Logger(Class myType) { + this.log = new Log(myType); + } + } + + /** External code could use aspectOf to get at the log, i.e. */ + Log l = Logger.aspectOf(com.bigboxco.Foo.class).log; + </pre> + + <p>The one open question that I see is how this should interact + with inner types. If a pertype aspect is created for an outer + type should advice in that aspect run for join points in inner + types? That is the behavior of the most common uses of this idiom. + </p> + + <h3><a name="CALLEE_SIDE_CALLS">Different callee-side call join + points</a></h3> + + <p> Much effort was made in AspectJ 1.0 to be able to advise as + many call join points as possible. When the code for the call was + passed to the compiler, ajc would allow call pointcuts could pick + out the corresponding call join point. When only the code for the + called method or constructor was passed to the compiler, however, + ajc would allow only certain call pointcuts to pick out the + corresponding join point, using an implementation technique called + "callee-side call join points". </p> + + <p> Because this was dependent on an implementation technique, it + was highlighted in the implementation limitations section of the + AspectJ documentation. The AspectJ 1.1 must make different + implementation decisions, and so will pick out different + callee-side call join points. </p> + + <p> For this release, however, the question is moot: + <a href="#NO_CALLEE_SIDE_CALL">No handling</a> of callee-side call + join points are implemented for AspectJ 1.1beta1. </p> + + <h3><a name="SINGLE_INTERCLASS_TARGET">One target for intertype + declarations</a></h3> + + <p> Intertype declarations (once called "introductions") in + AspectJ 1.1 can only have one target type. So the following code + intended to declare that there is a void doStuff() method on all + subtypes of Target is not legal AspectJ 1.1 code. + </p> + + <pre> aspect A { + public void Target+.doStuff() { ... } + } + </pre> + + <p> The functionality of "multi-intertype declarations" can be + recovered by using a helper interface. + </p> + + <pre> aspect A { + private interface MyTarget {} + declare parents: Target+ implements MyTarget; + public void MyTarget.doStuff() { ... } + } + </pre> + + <p> We believe this is better style in AspectJ 1.0 as well, as it + makes clear the static type of "this" inside the method body. + </p> + + <p> The one piece of functionality that can not be easily + recovered is the ability to add static fields to many classes. We + believe that the <a href="#PER_TYPE">pertype proposal</a> provides + this functionality in a much more usable form.</p> + + <h3><a name="UNAVAILABLE_JOIN_POINTS">No initializer execution join + points</a></h3> + + <p> AspectJ 1.1 does not consider initializer execution as a + principled join point. The collection of initializer code (the + code that sets fields with initializers and the code in non-static + initializer blocks) is something that makes sense only in Java + source code, not in Java bytecode. </p> + + <h3><a name="CONSTRUCTOR_EXECUTION_IS_BIGGER">Initializers run + inside constructor execution join points</a></h3> + + <p> The code generated by the initializers in Java source code now + runs inside of constructor execution join points. This changes + how before advice runs on constructor execution join points. + Consider: </p> + + <pre> class C { + C() { } + String id = "identifier"; // this assignment + // has to happen sometime + } + aspect A { + before(C c) this(c) && execution(C.new()) { + System.out.println(c.id.length()); + } + } + </pre> + + <p> In AspectJ 1.0, this will print "10", since id is assigned its + initial value prior to the before advice's execution. However, in + AspectJ 1.1, this will throw a NullPointerExcception, since "id" + does not have a value prior to the before advice's execution. + </p> + + <p> Note that the various flavors of after returning advice are + unchanged in this respect in AspectJ 1.1. Also note that this + only matters for the execution of constructors that call a + super-constructor. Execution of constructors that call a + this-constructor are the same in AspectJ 1.1 as in AspectJ 1.0. + </p> + + <p> We believe this difference should be minimal to real programs, + since programmers using before advice on constructor execution + must always assume incomplete object initialization, since the + constructor has not yet run. </p> + + <h3><a name="WITHIN_MEMBER_TYPES">Small limitations of the within + pointcut</a></h3> + + <p> Becuase of the guarantees made (and not made) by the Java + classfile format, there are cases where AspectJ 1.1 cannot + guarantee that the within pointcut designator will pick out all + code that was originally within the source code of a certain + type. + </p> + + <p> The non-guarantee applies to code inside of anonymous and + local types inside member types. While the within pointcut + designator behaves exactly as it did in AspectJ 1.0 when given a + package-level type (like C, below), if given a member-type (like + C.InsideC, below), it is not guaranteed to capture code in + contained local and anonymous types. For example: </p> + + <pre> class C { + Thread t; + class InsideC { + void setupOuterThread() { + t = new Thread( + new Runnable() { + public void run() { + // join points with code here + // might not be captured by + // within(C.InsideC), but are + // captured by within(C) + System.out.println("hi"); + } + }); + } + } + } + </pre> + + <p> We believe the non-guarantee is small, and we haven't verified + that it is a problem in practice. </p> + + <h3><a name="WITHIN_CODE">Small limitations of the withincode + pointcut</a></h3> + + <p>The withincode pointcut has similar issues to those described + above for within. We still need to do some hard thinking about + how anonymous and local classes can be handled by this pointcut. + <b>beta</b>: This pointcut is unimplementd in the beta1 release. + </p> + + <h3><a name="INSTANCEOF_ON_WILD">Can't do instanceof matching on + type patterns with wildcard</a></h3> + + <p>The pointcut designators this, target and args specify a + dynamic test on their argument. These tests can not be performed + on type patterns with wildcards in them. The following code that + compiled under 1.0 will be an error in AspectJ-1.1:</p> + + <pre> pointcut oneOfMine(): this(com.bigboxco..*); + </pre> + + <p>The only way to implement this kind of matching in a modular + way would be to use the reflection API at runtime on the Class of + the object. This would have a very high performance cost and + possible security issues. There are two good work-arounds. If + you control the source or bytecode to the type you want to match + then you can use declare parents, i.e.:</p> + + <pre> private interface OneOfMine {} + declare parents: com.bigboxco..* implements OneOfMine; + pointcut oneOfMine(): this(OneOfMine); + </pre> + + <p>If you want the more dynamic matching and are willing to pay + for the performance, then you should use the Java reflection API + combinded with if. That would look something like:</p> + + <pre> pointcut oneOfMine(): this(Object) && + if(classMatches("com.bigboxco..*", + thisJoinPoint.getTarget().getClass())); + + static boolean classMatches(String pattern, Class _class) { + if (patternMatches(pattern, _class.getName())) return true; + ... + } + </pre> + + <p>Note: wildcard type matching still works in all other pcds that + match based on static types. So, you can use + 'within(com.bigboxco..*+)' to match any code lexically within one + of your classes or a subtype thereof. This is often a good + choice.</p> + </p> + + + <h3><a name="NO_SOURCE_COLUMN">SourceLocation.getColumn()</a></h3> + + <p>The Java .class file format contains information about the + source file and line numbers of its contents; however, it has not + information about source columns. As a result, we can not + effectively support the accesss of column information in the + reflection API. So, any calls to + thisJoinPoint.getSourceLocation().getColumn() will be marked as + deprecated by the compiler, and will always return 0.</p> + + <h3><a name="ASPECT_PRECEDENCE">Aspect precedence</a></h3> + + <p> AspectJ 1.1 has a new declare form: + </p> + + <pre> declare dominates ":" TypePatternList ";" + </pre> + + <p> This is used to declare advice ordering constraints on join + points. For example, the constraints that (1) aspects that have + Security as part of their name should dominate all other aspects, and + (2) the Logging aspect (and any aspect that extends it) should + dominate all non-security aspects, can be expressed by: </p> + + <pre> declare dominates: *..*Security*, Logging+, *; + </pre> + + <p> In the TypePatternList, the wildcard * means "any type not matched + by another type in the declare dominates". </p> + + <h4>Various cycles</h4> + + <p> It is an error for any aspect to be matched by more than one + TypePattern in a single decare dominates, so: </p> + + <pre> declare dominates: A, B, A ; // error + </pre> + + <p> However, multiple declare dominates forms may legally have this + kind of circularity. For example, each of these declare dominates is + perfectly legal: + </p> + + <pre> declare dominates: B, A; + declare dominates: A, B; + </pre> + + <p> And a system in which both constraints are active may also be + legal, so long as advice from A and B don't share a join point. So + this is an idiom that can be used to enforce that A and B are strongly + independent. </p> + + <h4>Applies to concrete aspects</h4> + + <p> Consider the following library aspects: + </p> + + <pre> abstract aspect Logging { + abstract pointcut logged(); + + before(): logged() { + System.err.println("thisJoinPoint: " + thisJoinPoint); + } + } + + aspect aspect MyProfiling { + abstract pointcut profiled(); + + Object around(): profiled() { + long beforeTime = System.currentTimeMillis(); + try { + return proceed(); + } finally { + long afterTime = System.currentTimeMillis(); + addToProfile(thisJoinPointStaticPart, + afterTime - beforeTime); + } + } + abstract void addToProfile( + org.aspectj.JoinPoint.StaticPart jp, + long elapsed); + } + </pre> + + <p> In order to use either aspect, they must be extended with + concrete aspects, say, MyLogging and MyProfiling. In AspectJ + 1.0, it was not possible to express that Logging's advice (when + concerned with the concrete aspect MyLogging) dominated + Profiling's advice (when concerned with the concrete aspect + MyProfiling) without adding a dominates clause to Logging + itself. In AspectJ 1.1, we can express that constraint with a + simple: </p> + + <pre> declare dominates: MyLogging, MyProfiling; + </pre> + + <h4>Changing order of advice for sub-aspects</h4> + + <p> By default, advice in a sub-aspect has more precedence than + advice in a super-aspect. One use of the AspectJ 1.0 dominates + form was to change this precedence: + </p> + + <pre> abstract aspect SuperA dominates SubA { + pointcut foo(): ... ; + + before(): foo() { + // in AspectJ 1.0, runs before the advice in SubA + // because of the dominates clause + } + } + + aspect SubA extends SuperA { + before(): foo() { + // in AspectJ 1.0, runs after the advice in SuperA + // because of the dominates clause + } + } + </pre> + + <p> This no longer works in AspectJ 1.1, since declare dominates only + matters for concrete aspects. Thus, if you want to regain this kind + of precedence change, you will need to refactor your aspects. + </p> + + <h3><a name="MISSING_LANGUAGE">Language features unimplemented in + 1.1beta1</a></h3> + + <p>All of these language features should be implemented in + 1.1beta1.</p> + + <ul> + <li>per's: perthis, pertarget, percflow</li> + <li>pointcut designators: withincode, if, cflow, and cflowbelow</li> + <li>joinpoints and corresponding pcds: + initialization, preinitialization and handler</li> + <li>args pcd with multiple ..'s</li> + <li>privileged aspects</li> + <li>declare soft</li> + <li>inter-type constructor declarations</li> + </ul> + + <h3><a name="SOURCEROOT">The -sourceroots option</a></h3> + + <p> The AspectJ 1.1 compiler now accepts a -sourceroots option used to + pass all .java files in particular directories to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows). </p> + + <p> So, if you have your project separated into a gui module and a + base module, each of which is stored in a directory tree, you might + use one of + </p> + + <pre> ajc -sourceroots /myProject/gui:/myProject/base + ajc -sourceroots d:\myProject\gui;d:\myProject\base + </pre> + + <p> This option may be used in conjunction with lst files, listing + .java files on the command line, and the -injars option. + </p> + + <p> <strong>ALPHA:</strong> In the beta release of the compiler, + only one directory can be passed to the -sourceroots option. + </p> + + <h3><a name="BYTECODE_WEAVING">The -injars option</a></h3> + + <p> The AspectJ 1.1 compiler now accepts an -injars option used to + pass all .class files in a particular jar file to the compiler. It + takes either a single directory name, or a list of directory names + separated with the CLASSPATH separator character (":" for various + Unices, ";" for various Windows). </p> + + <p> So, if MyTracing.java defines a trace aspect that you want to + apply to all the classes in myBase.jar and myGui.jar, you would use + one of: </p> + + <pre> ajc -injars /bin/myBase.jar:/bin/myGui.jar MyTracing.java + ajc -injars d:\bin\myBase.jar;d:\bin\myGui.jar MyTracing.java + </pre> + + <p> The class files in the input jars must not have had advice woven + into them, since AspectJ enforces the requirement that advice is woven + into a particular classfile only once. So if the classfiles in the + jar file are to be created with the ajc compiler (as opposed to a pure + Java compiler), they should not be compiled with any non-abstract + aspects. </p> + + <p> This option may be used in conjunction with lst files, listing + .java files on the command line, and the -sourceroots option. + </p> + + <h3><a name="OUTJAR">The -outjar option</a></h3> + + <p> The -outjar option takes the name of a jar file into which the + results of the compilation should be put. For example: + + <pre> ajc -injars myBase.jar MyTracing.java -outjar myTracedBase.jar + </pre> + + <p> No meta information is placed in the output jar file </p> + + <h3><a name="INCREMENTAL">Incremental compilation</a></h3> + + <p> The AspectJ 1.1 compiler now supports incremental compilation. + For the final release, this will work from the various IDE plugins we + ship, but for the Alpha release, it is only supported on the + command-line compiler. + </p> + + <p> When ajc is called with the -incremental option, it must also be + passed a -sourceroots option specifying a directory to incrementally + compile. Once the initial compile is done, ajc waits for console + input. Every time it reads a new line, (i.e., every time the user + hits return) ajc recompiles those input files that need recompiling. + </p> + + <h4>Limitations for 1.1beta1</h4> + + <p> some changes to classes should force re-weaving, but are not + noticed</p> + + <p> inter-type declarations, declare parents are not tracked + correctly</p> + + + <h3><a name="NO_WEAVE">-noweave, a compiler option to suppress + weaving</a></h3> + + <p> The -noweave option suppresses weaving, and generates + classfiles and that can be passed to ajc again (through the + -injars option) to generate final, woven classfiles. </p> + + <h3><a name="BINARY_ASPECTS">-aspectpath, working with aspects in .class/.jar + form</a> </h3> + + <p> When aspects are compiled into classfiles, they include all + information necessary for the ajc compiler to weave their advice + and deal with their inter-type declarations. In order for these + aspects to have an effect on a compilation process, they must be + passed to the compiler on the -aspectpath. Every .jar file on + this path will be searched for aspects and any aspects that are + found will be enabled during the compilation. The binary forms of + this aspects will be untouched. </p> + + <h3><a name="NO_AROUND_OPTIMIZATION">Inlining around advice</a></h3> + + <p> In the beta release of the compiler, around advice was + implemented in the safest way for all uses of around advice. However, + many (if not most) case of around advice do not involve, for example, + inner classes capturing proceed, and can (and will) be implemented in + more efficient styles. </p> + + <h3><a name="NO_CALLEE_SIDE_CALL">Callee-side call join + points</a></h3> + + <p> The 1.0 implementation of AspectJ, when given: + </p> + + <pre> class MyRunnable implements Runnable { + public void run() { ... } + } + + aspect A { + call(): (void run()) && target(MyRunnable) { + // do something here + } + } + </pre> + + <p> would cause A's advice to execute even when, say, java.lang.Thread + called run() on a MyRunnable instance. The beta release of AspectJ + 1.1, however, does not expose any call join point unless it is given + the calling code. And the final release of AspectJ 1.1 may have + <a href="#CALLEE_SIDE_CALLS">different callee-side call behavior</a> + than the 1.0.6 release. </p> + + <h3><a name="OTHER_X_OPTIONS">Various -X options</a></h3> + + <p> The AspectJ 1.0 compiler supported a number of options that + started with X, for "experimantal". Some of them will not be + supported in 1.1, either because the "experiment" succeeded (in + which case it's part of the normal functionality) or failed. + Others will be supported as is (or nearly so) in 1.1: + </p> + + <ul> + <li>-XOcodeSize: Not supported in 1.1beta1, since we're not + doing <em>any</em> <a href="#NO_AROUND_OPTIMIZATION">inlining of + around advice</a> in beta. This may or may not be supported in + 1.1 final: bytecode weaving gives us more leeway in inlining + anyway, so this may be the default. </li> + + <li>-XtargetNearSource: Not suppoted in 1.1beta1, but will be + supported in 1.1 final. </li> + + <li>-XserializableAspects: Since perthis and pertarget aspects + are not implemented in 1.1beta1, this option has no meaning in + 1.1beta1. The intention is for 1.1 final to implement this + option just like the 1.0.6 compiler. </li> + + <li>-XaddSafePrefix: This option will not be supported in 1.1 at + all because we're now always using (what we believe to be) safe + prefixes. </li> + + <li>-Xlint: <a href="#XLINT">Will be more useful in + 1.1</a>. </li> + </ul> + + <h3><a name="ERROR_MESSAGES">Many confusing error messages in + 1.1beta1</a></h3> + + <p>Building on the eclipse compiler has given us access to a very + sophisticated problem reporting system as well as highly optimized + error messages for pure Java code. Often this leads to noticably + better error messages than from ajc-1.0.6. However, when we don't + handle errors correctly this can sometimes lead to cascading error + messages where a single small syntax error will produce dozens of + other messages.</p> + + <p>Many error condition in beta1 are signalled by exception. You + shouldn't be surprised to see RuntimeException("unimplemented") + produced from perfectly reasonable programs that use features we + just haven't implemented yet.</p> + + + <h3><a name="EXCEPTION_CHECKING">No exception checking during + weaving in 1.1beta1</a></h3> + + <p>Advice that has an explicit throws clause needs to have that + throws clause checked during weaving for each join point that is + matched. This checking is not implemented in 1.1beta1 which can + lead to checked exceptions being thrown from places they are not + allowed by the Java language.</p> + + <pre> before() throws IOException : execution (void m()) { + InputStream s = new FileInputStream("m.out"); + ... + } + ... + public void m() { ... } + </pre> + + <p>This code should result in a link-time weaving error that the + throws clause in the advice is incompatible with the checked + exceptions which can be legally thrown from the matched join + point. In beta1 this will just silently weave in the advice and + it will be possible for an IOException to be thrown from m().</p> + + <h3><a name="XLINT">-Xlint option partially supported in + 1.1beta1</a></h3> + + <p>-Xlint:ignore,error,warning will set the level for all Xlint + warnings. -Xlintfile:lint.properties allows fine-grained control. + In tools.jar, see org/aspectj/weaver/XlintDefault.properties for + the default behavior and a template to copy. </p> + + <p> In 1.1beta1, only two cases will signal Xlint warnings. We + expect this list to become much larger before 1.1final. Because + the configurability allows users to turn off warnings, we will + also be able to warn about more potentially dangerous situations, + such as the potentially unsafe casts used by very polymorphic uses + of proceed in around advice. + </p> + + <h3><a name="NO_SOURCE">Source-specific options</a></h3> + + <p> Because AspectJ 1.1beta1 does not generate source code after + weaving, the source-code-specific options -source, -usejavac, + -nocomment and -workingdir options are meaningless and so not + supported. </p> + + <h3><a name="NO_STRICT_LENIENT">The -strict and -lenient + options</a></h3> + + <p> Because AspectJ 1.1 uses the Eclipse compiler, which has its + own mechanism for changing strictness, we no longer support the + -strict and -lenient options. </p> + + <h3><a name="NO_PORTING">The -porting option</a></h3> + + <p> AspectJ 1.1 does not have a -porting option. We believe that + many useful porting information will be presented by the -Xlint + option when it is enabled. </p> + + <h3><a name="13_REQUIRED">J2SE 1.3 required</a></h3> + <p>Because we build on Eclipse, the compiler will no longer run under J2SE 1.2. + You must run the compiler (and all tools based on the compiler) + using J2SE 1.3 or later. </p> + +</body> </html>
\ No newline at end of file diff --git a/build/products/tools/dist/README-TOOLS.html b/build/products/tools/dist/README-TOOLS.html new file mode 100644 index 000000000..80f57081f --- /dev/null +++ b/build/products/tools/dist/README-TOOLS.html @@ -0,0 +1,323 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>AspectJ Installation Note</title> +<STYLE TYPE="text/css"> +<!-- + + /* FOR THE SDA PAGE */ + + /* + BODY {margin-top: 15px; margin-left: 15px; margin-right: 15px;} + */ + + A:link { + color:#4756AC; + } + A:visited { + color:#60657B; + } + A:hover { + color:red + } + + INPUT {font:12px "Courier New", sans-serif;} + + H2 { + font:18px/18px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:110%; + } + H3 { + font:18px/18px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:110%; + } + H4 { + font:15px/16px Verdana, Arial, Helvetica, sans-serif; + color:black; + font-weight:bold; + margin-left: 10px; + line-height:140%; + } + P { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + .paragraph { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + .smallParagraph { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + margin-right: 10px; + margin-left: 10px; + line-height:130%; + } + /* + LI { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + } + + UL { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + }*/ + + DL { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + margin-right: 10px; + margin-left: 15px; + line-height:120%; + } + B { font:13px/13px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:140%; + } + .footer { + font:10px/10px Verdana, Arial, Helvetica, sans-serif; + color:#888888; + text-align:left + } + .figureTitle { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + text-align:justify; + text-align:center + } + .copyrightNotice { + font:10px/10px Verdana, Arial, Helvetica, sans-serif; + color:#999999; + line-height:110%; + } + .smallHeading { + font:13px/13px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:110%; + } + .tinyHeading { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + font-weight:bold; + line-height:120%; + } + .newsText { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + line-height:130%; + } + .smallParagraph { + font:11px/11px Verdana, Arial, Helvetica, sans-serif; + line-height:130%; + } + .fancyHeading { + font:20px/20px Chantilly, Arial, Helvetica, sans-serif; + margin-right: 10px; + color:#6f7a92; + margin-left: 10px; + line-height:130%; + } + +--> +</STYLE> +</head> + +<BODY BGCOLOR="white"> + +<h2 align="center">AspectJ<sup><small>TM</small></sup> Compiler and Core Tools</h2> + +<p align="center"><i>Version @build.version.long@ released on @build.date@.</i></p> + +<h3>1 Contents of this Package</h3> + +<UL> + <li>in the directory <CODE><aspectj install dir>/bin</CODE> + <ul> + <li><CODE>ajc</CODE>: compiler for the AspectJ language</li> + + <!-- XXX removed references to ajdoc and ajdb --> + <li><CODE>ajbrowser</CODE>: GUI for compiling programs with + <CODE>ajc</CODE> and navigating the + crosscutting structure</li> + </ul> + + <li>in the directory <CODE><aspectj install dir>/lib</CODE> + <ul> + <li><CODE>aspectjtools.jar</CODE>: libraries for the + AspectJ compiler, core tools, and Ant tasks</li> + + <li><CODE>aspectjrt.jar</CODE>: libraries for compiled + AspectJ code</li> + </ul> + </li> +</UL> + +<hr> + +<h3>2 Install Procedure</h3> + +<p>After finishing automatic installation, we recommend that you perform the +following steps to complete your installation:</p> + + <p><b>2.1 Add <CODE><aspectj install dir>/lib/aspectjrt.jar</CODE> to your class path</b></p> + +<blockquote> + <p>This .jar file contains several small classes that are + required to compile programs with the ajc compiler, and are the only + extra classes necessary to run programs compiled with the ajc + compiler. You must have these classes somewhere on your class path + when running programs compiled with ajc. For detailed instructions + please see the <a href="#configInstructions">Configuration + Instructions</a> at the bottom of this document.</p> +</blockquote> + + <p><b>2.2 Put the AspectJ <code>bin</code> directory on your PATH</b></p> + +<blockquote> + <p>Modify your PATH to include <code><b><aspectj install + dir>/bin</b></code>. This will make it easier to run ajc. + For detailed instructions please see the + <a href="#configInstructions">Configuration Instructions</a> at the + bottom of this document.</p> +</blockquote> + +<p><b>2.3 Review the documentation and examples </p> + +<blockquote> + <p>A tutorial, development and programming guide, and example programs + are available in <a href="docs/index.html">docs</a> + and <a href="examples/">examples</a> +</blockquote> + +<p>If you did not use the automatic installation process, you may wish +to create short launch scripts to start ajc easily (section 3).</p> + +<hr> + +<h3>3. Running the Tools</h3> + +<p>If you did not use the automatic installation process or the +default launch scripts do not work on your system, you may wish to +create short launch scripts to start ajc easily. </p> + +<p>The AspectJ compiler and tools are Java programs that you can +run directly. You can run the <code>aspectjtools.jar</code> directly +using java's -jar option: + +<blockquote> +<CODE>C:\jdk1.3\bin\java.exe -jar D:\aspectj\lib\aspectjtools.jar %*</CODE> +</blockquote> + +With no arguments or only argument list (.lst) files, this will launch +<code>ajbrowser</code>, the GUI structure browser; otherwise, it will +launch <code>ajc</code>, the AspectJ compiler. + +<p> You can also create scripts like those created by the installer. +These scripts use full paths that are system dependent so you will +likely have to change these. </p> + +<p>Here's a sample launch script for WinNT and Win2K (note that this +is single line in the .bat file):</p> + +<blockquote> +<CODE>C:\jdk1.3\bin\java.exe -classpath D:\aspectj\lib\aspectjtools.jar -Xmx64M +org.aspectj.tools.ajc.Main %*</CODE> +</blockquote> + +<p>Here's a sample launch script for a Unix shell (on Linux using Sun's JDK1.3):</p> + +<blockquote> +<CODE>/usr/java/jdk1.3/jre/bin/java -classpath /home/aspectj/lib/aspectjtools.jar -Xmx64M org.aspectj.tools.ajc.Main "$@"</CODE> +</blockquote> + +<hr> +<h3><a name="configInstructions">4. Configuration Instructions</a></h3> + +<h4>4.1 Adding <code><aspectj install dir>/lib/aspectjrt.jar</code> to your classpath</h4> + +<p>There are several ways to add this jar file to your classpath:</p> + +<ul> + <li> + copy <code>aspectjrt.jar</code> to the <code>jdk/jre/lib/ext</code> directory</li> + <li> + add <code>aspectjrt.jar</code> to your CLASSPATH + environment variable (see the next section for details)</li> + <li> + always use the "<code>-classpath aspectjrt.jar</code>" option when running + programs compiled with ajc</li> +</ul> +<h4>4.2 <A NAME="6.1">Setting the Environment Variables on Windows</A></h4> + +<P>The following instructions use the PATH variable as an example, but +this process is identical for the CLASSPATH variable. </P> + +<P>You can do the variables permanently for all the shells that you +will run or temporarily only for the current shell. To change the +PATH only in the current shell, and assuming you've installed AspectJ +in <code>C:\apps\aspectj</code>, type:</P> + +<blockquote><code>> set PATH=%PATH%;C:\apps\aspectj\bin</code></blockquote> + +<P><b>Changing Environment Variables Permanently on WinNT and Win2000</b> +<ul> + <li> + <p class="paragraph">open the Environment Variables dialog + <ul> + <li> + <p class="paragraph">WinNT: in "Control Panels" double-click + "System" and select the "Environment" tab</li> + <li> + <p class="paragraph">Win2K: in "Control Panels" double-click + "System", select the "Advanced" tab and click the + "Environment Variables..." button</li> + </ul> + </li> + <li> + <p class="paragraph">select the environment variable for editing, or add it + using the "New..." button if it does not exist</li> + <li> + <p class="paragraph">add the new entry separated from the others by a + semi-colon (;) and close the dialog</li> + <li> + <p class="paragraph">note that the change will not affect shells that were + already running </li> +</ul> +<P><b>Changing Environment Variables Permanently on Win9x</b> +<ul> + <li> + <p class="paragraph">open the AUTOEXEC.BAT with an editor such as NotePad</li> + +<LI> + <p class="paragraph">edit the PATH statement to include the new entry and save + the file, e.g.<BR> + +<blockquote> + <CODE>PATH C:\WINDOWS;C:\WINDOWS\COMMAND;C:\;C:\DOS;C:\apps\aspectj\bin</CODE> + </blockquote> +</LI> +<LI> + <p class="paragraph">note that the change will not affect shells that were + already running +</ul> + +</body> + +</html> diff --git a/build/products/tools/dist/lib/aspectjrt.jar b/build/products/tools/dist/lib/aspectjrt.jar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/products/tools/dist/lib/aspectjrt.jar diff --git a/build/products/tools/dist/lib/aspectjtools.jar b/build/products/tools/dist/lib/aspectjtools.jar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/products/tools/dist/lib/aspectjtools.jar diff --git a/build/products/tools/install/configure-auto.html b/build/products/tools/install/configure-auto.html new file mode 100644 index 000000000..7abdaac6b --- /dev/null +++ b/build/products/tools/install/configure-auto.html @@ -0,0 +1,17 @@ +<html> + +<head> +<title>auto configure</title> +</head> + +<body> + +<p>The installer has successfully found the path to your Java home (J2SE 1.3 or greater). +This path will be used as the default Java location when generating +script files for launching the AspectJ compiler and core tools. Unless +you know that this path is wrong, we suggest that you press +<b>Next</b> to continue.</p> + +</body> + +</html> diff --git a/build/products/tools/install/configure-hand.html b/build/products/tools/install/configure-hand.html new file mode 100644 index 000000000..73b3e2185 --- /dev/null +++ b/build/products/tools/install/configure-hand.html @@ -0,0 +1,21 @@ +<html> + +<head> +<title>configure</title> +</head> + +<body> + +<p>The installer cannot find the path to your Java home (J2SE 1.3 or greater). +Please try to find this directory on your path now. It will probably look +something like <code>jdk1.3.1</code>. If you can't find this +directory now, you may continue with the install, but you will have +to either edit your launch scripts by hand or to set the JAVA_HOME +environment variable to point to the right location. +</p> + +<p>Press <b>Next</b> when ready to continue.</p> + +</body> + +</html> diff --git a/build/products/tools/install/finish.html b/build/products/tools/install/finish.html new file mode 100644 index 000000000..5e7b60c60 --- /dev/null +++ b/build/products/tools/install/finish.html @@ -0,0 +1,26 @@ +<html> + +<head> +<title>finish</title> +</head> + +<body> + +<p>The automatic installation process is complete. There are still three +steps that we recommend you perform to complete the installation:</p> +<ol> + <li>Put <b><code>${installer.output.aspectjrt}</code></b> on your CLASSPATH. + This 29KB .jar file contains classes that are required by any program compiled + with the ajc compiler.</li> + <li>Modify your PATH to include <code> <b>${installer.output.dir.bin}</b></code>. + This will make it easier to run ajc. </li> + <li>Download the documentation and examples from http://aspectj.org + to learn how to use the language.</li> +</ol> + +<p>These steps are described in more detail in <code> +<b>${installer.output.readme}</b></code>.</p> + +</body> + +</html> diff --git a/build/products/tools/install/install-finish.html b/build/products/tools/install/install-finish.html new file mode 100644 index 000000000..26aea1e5e --- /dev/null +++ b/build/products/tools/install/install-finish.html @@ -0,0 +1,20 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p> </p> + +<p>The automatic installation process is complete. Press <b>Next </b>for +some important final instructions.</p> + +</body> + +</html> diff --git a/build/products/tools/install/install-start.html b/build/products/tools/install/install-start.html new file mode 100644 index 000000000..858ed8512 --- /dev/null +++ b/build/products/tools/install/install-start.html @@ -0,0 +1,21 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p> </p> + +<p>Now installing to ${installer.output.dir}.</p> + +<p>Press <b>Cancel</b> to interrupt the installation.</p> + +</body> + +</html> diff --git a/build/products/tools/install/intro.html b/build/products/tools/install/intro.html new file mode 100644 index 000000000..d64b62523 --- /dev/null +++ b/build/products/tools/install/intro.html @@ -0,0 +1,28 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<h2 align="center">Installer for AspectJ(TM) Compiler and Core Tools</h2> + +<p align="center">Version ${build.version.long} built on ${build.date}</p> +<p>These tools are covered by the Mozilla Public License (see +http://aspectj.org/MPL). +This is a binary only release. You can download the source code at +http://aspectj.org</p> +<p>${copyright.allRights.from1998}</p> +<p></p> + +<p>Press <b>Next</b> to continue. At any time you may press <b>Cancel</b> +to exit the installation process.</p> + +</body> + +</html> diff --git a/build/products/tools/install/location.html b/build/products/tools/install/location.html new file mode 100644 index 000000000..1ac230abf --- /dev/null +++ b/build/products/tools/install/location.html @@ -0,0 +1,20 @@ +<html> + +<head> +<meta http-equiv="Content-Language" content="en-us"> +<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> +<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> +<meta name="ProgId" content="FrontPage.Editor.Document"> +<title>New Page 1</title> +</head> + +<body> + +<p>Please select a directory into which to install + the AspectJ compiler and core tools.</p> +<p>Press <b>Install</b> to begin the installation process + to this directory.</p> + +</body> + +</html> diff --git a/build/scripts/lcp.bat b/build/scripts/lcp.bat new file mode 100644 index 000000000..24eee9b3d --- /dev/null +++ b/build/scripts/lcp.bat @@ -0,0 +1,2 @@ +set LCP=%LCP%;%1
+
diff --git a/build/scripts/localSetup.bat.txt b/build/scripts/localSetup.bat.txt new file mode 100644 index 000000000..b2b738334 --- /dev/null +++ b/build/scripts/localSetup.bat.txt @@ -0,0 +1,19 @@ + +rem sample local setup, e.g., for quicklook.bat +rem set values and change filename to localSetup.bat + +rem -- picked up by ant scripts +set JAVA_HOME=c:\apps\jdk1.3.1 + +rem -- quicklook.bat adds the following to command line + +rem setup from/to for result email +set userEmail=isberg@parc.com +set mailHost=dagobah.parc.xerox.com + +rem setup ant cvs task, which execs cvs +set HOME=c:\home\wes +set CVSROOT=:ext:isberg@aspectj.org:/aspectj-home/cvsroot +set CVS_RSH=c:\apps\ssh\ssh +set PATH=c:\home\apps\cygwin\bin;c:\WIN2000\system32;c:\WIN2000 + diff --git a/build/scripts/quicklook.bat b/build/scripts/quicklook.bat new file mode 100644 index 000000000..7ac007257 --- /dev/null +++ b/build/scripts/quicklook.bat @@ -0,0 +1,99 @@ +rem be verbose, no @echo off
+rem requires existing ajhome install for ant scripts, libs
+rem beware - withou DOS linefeeds, DOS won't interpret...
+
+rem ------ read variables from local setup
+set scriptPath=%~dp0
+set scriptDir=%scriptPath:\quicklook.bat=%
+if not exist %scriptDir%\localSetup.bat goto ERR_SETUP
+
+call %scriptDir%\localSetup.bat
+
+if "x" == "x%userEmail%" goto ERR_VARIABLE
+if "x" == "x%mailHost%" goto ERR_VARIABLE
+if "x" == "x%HOME%" goto ERR_VARIABLE
+if "x" == "x%CVS_RSH%" goto ERR_VARIABLE
+if "x" == "x%CVSROOT%" goto ERR_VARIABLE
+
+set buildDir=%scriptDir:\scripts=%
+set antScript=%buildDir:\build=%\lib\ant\bin\ant.bat
+
+if not exist %antScript% goto ERR_ANT
+if not exist %buildDir% goto ERR_BUILDDIR
+if not exist %JDKDIR% goto ERR_JDKDIR
+
+rem XXX redo these when web building
+rem set SQEDIRPATH=%SQEDRIVE%%SQEDIR%
+rem if exist %SQEDIRPATH% goto RUN
+rem net use %SQEDRIVE% \\samba\aop /persistent:no
+rem if not exist %SQEDIRPATH% goto ERR_MOUNT
+rem set mountedDrive=yes
+goto RUN
+
+rem build update.tree, quicklook
+:RUN
+if not "x" == "x%DEBUG%" set verbose=-verbose
+chdir %buildDir% || goto ERR_CD
+rem fyi, normal ant.bat script loses any internal "=", so we rely on a patched script +set MAIL_OPTIONS=-logger org.apache.tools.ant.listener.MailLogger -DMailLogger.mailhost=%mailHost% -DMailLogger.success.to=%userEmail% -DMailLogger.failure.to=%userEmail% -DMailLogger.from=%userEmail%
+set options=%MAIL_OPTIONS% -DCVSROOT=%CVSROOT%
+rem cannot use "|| goto ERR_ANT_UPDATE" b/c ant script returns non-zero for valid builds
+call %antScript% -f build.xml update.tree %verbose% %options% +
+set options=%MAIL_OPTIONS% -Duser.email=%userEmail% -Daop.dir=%SQEDRIVE%
+set options=%options% -Drun.ajcTests=runAjcTests -Drun.junit.tests=runJUnitTests
+call %antScript% -f build.xml quicklook %verbose% %options% +
+if "ok%mountedDrive%" == "okyes" net use %SQEDRIVE% /delete
+goto QUIT
+
+rem ---------- errors
+:ERR_CD
+echo "unable to cd to build directory: %buildDir%"
+goto QUIT
+
+:ERR_SETUP
+echo "expected local setup in %scriptDir%\localSetup.bat"
+goto QUIT
+
+:ERR_BUILDDIR
+echo "expected build dir: %buildDir%"
+goto QUIT
+
+:ERR_VARIABLE
+echo "local setup is incorrect - missing variables"
+goto QUIT
+
+:ERR_ANT
+echo "expected ant script: %antScript%"
+goto QUIT
+
+:ERR_JDKDIR
+echo "no JDKDIR=%JDKDIR%"
+goto QUIT
+
+:ERR_MOUNT
+echo "unable to mount or use SQEDIRPATH=%SQEDIRPATH%"
+goto QUIT
+
+:ERR_CREATE_FAILED
+echo "unable to find quicklook source after running setup build"
+goto QUIT
+
+:ERR_ANT_CREATE
+echo "FAIL ant create.source failed"
+goto QUIT
+
+:ERR_ANT_UPDATE
+echo "FAIL ant update.source failed"
+goto QUIT
+
+:ERR_ANT_QUICKLOOK
+echo "FAIL ant quicklook failed"
+goto QUIT
+
+:ERR_ANT_DESTORY
+echo "FAIL ant destroy.source failed"
+goto QUIT
+
+:QUIT
diff --git a/build/scripts/runQuicklook.bat b/build/scripts/runQuicklook.bat new file mode 100644 index 000000000..841bb57ea --- /dev/null +++ b/build/scripts/runQuicklook.bat @@ -0,0 +1,7 @@ +rem @echo off
+
+rem %~dp0 is name of current script
+set scriptPath=%~dp0
+set scriptDir=%scriptPath:\runQuicklook.bat=%
+
+call %scriptDir%quicklook.bat > %scriptDir%quicklook.out 2>&1
diff --git a/build/src/$installer$/org/aspectj/Main.java b/build/src/$installer$/org/aspectj/Main.java new file mode 100644 index 000000000..b7cdda94e --- /dev/null +++ b/build/src/$installer$/org/aspectj/Main.java @@ -0,0 +1,1699 @@ +/* ******************************************************************* + * Copyright (c) 2000-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + + +package $installer$.org.aspectj; + +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.URL; +import java.util.zip.*; +import java.util.*; +import java.lang.reflect.InvocationTargetException; + +/** + * Invoke the Installer gui. + * To run without gui, pass two arguments: + * <li> "-text".equals(arg[0])</li> + * <li> arg[1] is the path to a properties file which defines + * name="output.dir" value="{path to output dir}" + * name="context.javaPath" value="{path to JDKDIR}", i.e,. + * <pre>output.dir=c:/latest + * "context.javaPath=c:/apps/jdk1.3.1</pre></li> + * <li>outputDir must be created and empty (i.e., no overwriting</li> + * <li>the VM being invoked should be the target vm</li> + */ +public class Main { + public static void main(String[] args) { + Options.loadArgs(args); + boolean hasGui = (null == Options.textProperties); + Properties properties = new Properties(); + InputStream istream = null; + try { + istream = Main.class.getResourceAsStream(Installer.RESOURCE_DIR + "/properties.txt"); + if (istream == null) { + System.err.println("unable to load properties.txt using Main.class - exiting"); + Main.exit(-1); + } + properties.load(istream); + if (!hasGui) { + istream.close(); + istream = new FileInputStream(Options.textProperties); + properties.load(istream); + } + istream.close(); + istream = null; + } catch (IOException ioe) { + handleException(ioe); + } finally { + if (null != istream) { + try {istream.close();} + catch (IOException e){} // ignore + } + } + + try { + String className = (String)properties.get("installer.main.class"); + //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + Installer installer = (Installer)Class.forName(className).newInstance(); + InstallContext installerContext = new InstallContext(properties); + installerContext.setHasGui(hasGui); + installer.setContext(installerContext); + if (installerContext.hasGui()) { // let context force whether or not to run gui + installer.runGUI(); + } else { + // set output dir and java path in context after minimal validation + String propName = "output.dir"; + String propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String outputDirName = propValue; + propName = "context.javaPath"; + propValue = properties.getProperty(propName); + if (null == propValue) { + throw new Exception("expecting property " + propName); + } + String javaPath = propValue; + File outputDir = new File(outputDirName); + if (! outputDir.isDirectory()) { + throw new Exception("not a dir outputDirName: " + outputDirName + + " dir: " + outputDir); + } + if (! outputDir.canWrite()) { + throw new Exception("cannot write outputDirName: " + outputDirName + + " dir: " + outputDir); + } + InstallContext context = installer.getContext(); // todo: why not use installerContext? + context.setOutputDir(outputDir); + context.javaPath = new File(javaPath); + // todo: check javaPath for ... bin/java? lib/rt.jar? + if (! outputDir.isDirectory() || ! outputDir.canRead()) { + throw new Exception("invalid javaPath: " + javaPath); + } + // directly set context and run + installer.getInstallPane().setContext(installerContext); + installer.run(); + } + } catch (Exception e) { + handleException(e); + } + } + + public static void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + Main.exit(-1); + } + + /** indirection for System.exit - todo apply cleanup here as necessary */ + public static void exit(int value) { + System.exit(value); + } +} // class Main + +class Options { + public static boolean verbose = false; + public static String textProperties = null; + public static boolean forceError1 = false; + public static boolean forceError2 = false; + public static boolean forceHandConfigure = false; + + public static void loadArgs(String[] args) { + if (args == null) return; + for (int i=0; i<args.length; i++) { + String arg = args[i]; + if (arg == null) continue; + + if (arg.equals("-verbose")) verbose = true; + else if (arg.equals("-forceError1")) forceError1 = true; + else if (arg.equals("-forceError2")) forceError2 = true; + else if (arg.equals("-forceHandConfigure")) forceHandConfigure = true; + else if (arg.equals("-text")) { + if (i+1 < args.length) { + textProperties = args[++i]; + } + } + } + } +} + +class ToolsInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Compiler and Core Tools Installer"; } + + public String getPrefix() { return "tools"; } + + public ToolsInstaller() { + InstallPane installPane = new InstallPane(true); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new ConfigureLauncherPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + +class DocsInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Documentation and Examples Installer"; } + + public String getPrefix() { return "docs"; } + + public DocsInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + +class AJDEForJBuilderInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Support for JBuilder"; } + + public String getPrefix() { return "ajdeForJBuilder"; } + + public AJDEForJBuilderInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\JBuilder6\\lib\\ext", + "c:\\apps\\JBuilder6\\lib\\ext", + "c:\\Program Files\\JBuilder6\\lib\\ext" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) return paths[pathIndex]; + } + return "c:\\JBuilder6\\lib\\ext"; + } else { + return "/usr/JBuilder6/lib/ext"; + } + } + + /** + * Make sure that the old jar file gets removed. + */ + public void verify() { + File jbuilder = new File(location.getText() + "/../../lib/jbuilder.jar"); + if (!jbuilder.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog + (frame, + "The location you specified does not seem to be a " + + "valid JBuilder install directory." + + " Continue?", "Confirm Install", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + + File oldFile = new File(location.getText() + "/ajbuilder.jar"); + if (oldFile.exists() && hasGui()) { + int ret = JOptionPane.showConfirmDialog(frame, + "This old version of AJDE for JBuilder (\"ajbuilder.jar\") exists" + + " and must be removed from the install directory." + + " OK to delete?", "Confirm Delete", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + oldFile.delete(); + } + } + } + }, + installPane, + new FinishPane() + }; + } +} + +class AJDEForForteInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Support for Forte 4J"; } + + public String getPrefix() { return "ajdeForForte"; } + + private String installLoc = ""; + + public AJDEForForteInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane() { + public String getDefaultLocation() { + if (context.onWindows()) { + // check some default locations + String[] paths = { "c:\\forte4j\\modules", + "c:\\apps\\forte4j\\modules", + "c:\\Program Files\\forte4j\\modules" }; + int pathIndex = 0; + for (; pathIndex < paths.length; pathIndex++) { + if (new File(paths[pathIndex]).exists()) return paths[pathIndex]; + } + return "c:\\forte4j\\modules"; + } else { + return "/usr/forte4j/modules"; + } + } + + public void verify() { + File forte = new File(location.getText() + "/../lib/openide.jar"); + installLoc = location.getText(); + if (!forte.exists() && hasGui()) { + int ret = + JOptionPane.showConfirmDialog + (frame, + "The location you specified does not seem to be a " + + "valid Forte install directory." + + " Continue?", "Confirm Install", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret != JOptionPane.YES_OPTION) { + Main.exit(-1); + } else { + // do nothing + } + } + } + }, + installPane, + new FinishPane() { + public void finalActions() { // todo verify dir ../lib/ext exists? + // !!! this should be done with two install locations, not by moving a file + new File(installLoc + "/../lib/ext/aspectjrt.jar").delete(); + new File(installLoc + "/aspectjrt.jar").renameTo(new File((installLoc + "/../lib/ext/aspectjrt.jar"))); + new File(installLoc + "/aspectjrt.jar").delete(); + } + } + }; + } +} + +class SrcInstaller extends Installer { + public String getTitle() { return "AspectJ(TM) Compiler and Core Tools Sources Installer"; } + + public String getPrefix() { return "sources"; } + + public SrcInstaller() { + InstallPane installPane = new InstallPane(false); + setInstallPane(installPane); + panes = new WizardPane[] { + new IntroPane(), + new LocationPane(), + installPane, + new FinishPane() + }; + } +} + + +abstract class Installer { + static final String EXIT_MESSAGE = "Are you sure you want to cancel the installation?"; + static final String EXIT_TITLE = "Exiting installer"; + /** relative directory in jar from package $installer$.org.aspectj + for loading resources - todo must be tracked during build */ + public static final String RESOURCE_DIR = "resources"; + + JFrame frame; + InstallContext context; + /** special pane that actually does the installation */ + InstallPane installPane; + + public Installer() { + } + + protected void setInstallPane(InstallPane installPane) { + this.installPane = installPane; + } + public InstallPane getInstallPane() { + return installPane; + } + + /** directly run the install pane, if any */ + public void run() { + if (null != installPane) { + installPane.run(); + } + } + + public abstract String getPrefix(); + + public void setContext(InstallContext context) { + this.context = context; + context.installer = this; + } + + public InstallContext getContext() { + return context; + } + + public String getTitle() { return "AspectJ(TM) Installer"; } + public int getWidth() { return 640; } + public int getHeight() { return 460; } + + protected WizardPane[] panes = new WizardPane[0]; + public WizardPane[] getPanes() { + return panes; + } + + public int findPaneIndex(WizardPane pane) { + for (int i = 0; i < panes.length; i++) { + if (panes[i] == pane) return i; + } + return -1; + } + + Component header, footer, body; + + public void runGUI() { + frame = new JFrame(getTitle()); + + if (Options.forceError1) { + throw new RuntimeException("forced error1 for testing purposes"); + } + + Dimension size = Toolkit.getDefaultToolkit().getScreenSize(); + + int x = (int)(size.getWidth() - getWidth()) / 2; + int y = (int)(size.getHeight() - getHeight()) / 2; + + //include a few sanity checks on starting position + if (x < 0) x = 0; + if (x > 600) x = 600; + if (y < 0) y = 0; + if (y > 400) y = 400; + + frame.setLocation(x, y); + frame.setSize(getWidth(), getHeight()); + moveToPane(getPanes()[0]); + frame.setVisible(true); + } + + public void moveToPane(WizardPane pane) { + pane.setContext(this.context); + + Dimension size = frame.getContentPane().getSize(); + + JPanel contents = new JPanel(); + contents.setLayout(new BorderLayout()); + header = makeHeader(); + contents.add(header, BorderLayout.NORTH); + + body = pane.getPanel(); + contents.add(body, BorderLayout.CENTER); + + footer = pane.getButtons(); + contents.add(footer, BorderLayout.SOUTH); + + contents.revalidate(); + contents.setSize(size); + + frame.setContentPane(contents); + + //XXX deal with threading here? + pane.run(); + } + + public Icon loadImage(String name) { + return new javax.swing.ImageIcon(this.getClass().getResource(name)); + } + + public Component makeHeader() { + return new JLabel(loadImage(Installer.RESOURCE_DIR + "/aspectjBanner.gif")); + } + + public ActionListener makeNextAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) + 1; + if (nextPaneIndex >= getPanes().length) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + pane.finish(); + moveToPane(nextPane); + } + }; + } + + public ActionListener makeBackAction(final WizardPane pane) { + int nextPaneIndex = findPaneIndex(pane) - 1; + if (nextPaneIndex < 0) { + return null; + } + + final WizardPane nextPane = getPanes()[nextPaneIndex]; + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + moveToPane(nextPane); + } + }; + } + + public ActionListener makeCancelAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + int ret = JOptionPane.showConfirmDialog(frame, EXIT_MESSAGE, EXIT_TITLE, + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + if (ret == JOptionPane.YES_OPTION) { + Main.exit(-1); + } + } + }; + } + public ActionListener makeFinishAction(WizardPane pane) { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + Main.exit(0); + } + }; + } +} + + + + // willing to go up to 3 levels deep to find either jre or jdk + + // jre\[*\]lib\ext + // jdk*\lib\tools.jar + + /***** + final static int MAX_DEPTH = 4; + public static void findPaths(String prefix, File currentDir, int currentDepth) { + if (currentDepth > MAX_DEPTH) return; + if (!currentDir.exists() || !currentDir.isDirectory()) return; + File [] files = currentDir.listFiles(); + if (files == null) return; + for (int i=0; i<files.length; i++) { + if (files[i] == null) continue; + if (!files[i].isDirectory()) continue; + if (files[i].getName().startsWith(prefix)) { + System.out.println("found: " + files[i]); + } else { + findPaths(prefix, files[i], currentDepth + 1); + } + } + } + + public static void findPaths(String prefix) { + File [] files = File.listRoots(); + for (int i=1; i<files.length; i++) { + if (!files[i].isDirectory()) continue; + if (files[i].getName().toLowerCase().startsWith(prefix)) { + System.out.println("found: " + files[i]); + } else { + findPaths(prefix, files[i], 1); + } + } + } + *****/ + +class InstallContext { + public InstallContext(Map properties) { + this.properties = properties; + properties.put("user.home", System.getProperty("user.home")); + //System.out.println("new install context"); + } + + private File outputDir; + public void setOutputDir(File outputDir) { + this.outputDir = outputDir; + + properties.put("installer.output.dir", outputDir.getAbsolutePath()); + properties.put("installer.output.dir.bin", new File(outputDir, "bin").getAbsolutePath()); + properties.put("installer.output.dir.doc", new File(outputDir, "doc").getAbsolutePath()); + properties.put("installer.output.aspectjrt", new File(new File(outputDir, "lib"), "aspectjrt.jar").getAbsolutePath()); + properties.put("installer.output.readme", new File(outputDir, "README-" + installer.getPrefix().toUpperCase()+".html").getAbsolutePath()); + } + + public File getOutputDir() { + return outputDir; + } + + private boolean hasGui; + public File javaPath; + public File toolsJarPath; + + public Installer installer; + + private Map properties; + + public boolean hasGui() { + return hasGui; + } + + public void setHasGui(boolean hasGui) { + if (this.hasGui != hasGui) { + this.hasGui = hasGui; + } + } + + public Font getFont() { + return new Font("Serif", Font.PLAIN, 14); + } + + public String getOS() { + return System.getProperty("os.name"); + } + + public boolean onOS2() { + return getOS().equals("OS2") || getOS().equals("OS/2"); + } + + public boolean onWindows() { + return getOS().startsWith("Windows") || onOS2(); + } + + public boolean onWindowsPro() { + return getOS().equals("Windows NT") || getOS().equals("Windows 2000"); + } + + public boolean onMacintosh() { + return getOS().startsWith("Mac"); + } + + public boolean onUnix() { + return !onMacintosh() && !onWindows(); + } + + static final String[] TEXT_EXTENSIONS = { + ".txt", ".text", ".htm", ".html", ".java", ".ajava", "README", ".lst" + }; + + public boolean isTextFile(File file) { + String name = file.getName(); + + for (int i=0; i < TEXT_EXTENSIONS.length; i++) { + if (name.endsWith(TEXT_EXTENSIONS[i])) return true; + } + + return false; + } + + public void handleException(Throwable e) { + System.out.println("internal error: " + e.toString()); + e.printStackTrace(); + if (hasGui()) { + JOptionPane.showMessageDialog(installer.frame, e.toString(), + "Unexpected Exception", JOptionPane.ERROR_MESSAGE); + } + } + + final static String OVERWRITE_MESSAGE = "Overwrite file "; + final static String OVERWRITE_TITLE = "Overwrite?"; + + final static String[] OVERWRITE_OPTIONS = { + "Yes", "No", "Yes to all" //, "No to all" + }; + + final static int OVERWRITE_YES = 0; + final static int OVERWRITE_NO = 1; + final static int OVERWRITE_ALL = 2; + //final static int OVERWRITE_NONE = 3; + + int overwriteState = OVERWRITE_NO; + boolean shouldOverwrite(final File file) { + //System.out.println("overwrite: " + file + " state " + overwriteState); + if (overwriteState == OVERWRITE_ALL) return true; + //if (overwriteState == OVERWRITE_NONE) return false; + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + int ret = JOptionPane.showOptionDialog(installer.frame, + OVERWRITE_MESSAGE+file.getPath(), OVERWRITE_TITLE, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, + OVERWRITE_OPTIONS, OVERWRITE_OPTIONS[OVERWRITE_YES]); + + overwriteState = ret; + } + }); + } catch (InvocationTargetException ite) { + handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + + return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; + } + + + public Map getProperties() { + return properties; + } +} + +abstract class WizardPane { + InstallContext context; + + protected JButton backButton = null; + protected JButton nextButton = null; + protected JButton cancelButton = null; + + public void setContext(InstallContext context) { this.context = context; } + + public abstract JPanel makePanel(); + + protected JTextArea makeTextArea(String data) { + JTextArea text = new JTextArea(data); + text.setOpaque(false); + text.setFont(context.getFont()); + text.setEditable(false); + return text; + } + + /** @return false only if there is an InstallContext saying there is no GUI */ + protected boolean hasGui() { + final InstallContext icontext = context; + return ((null == icontext) || icontext.hasGui()); + } + + public static String stringFromStream(InputStream stream) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + StringBuffer ret = new StringBuffer(); + int data; + while ( (data = reader.read()) != -1) { + ret.append( (char)data ); + } + return ret.toString(); + } + + public static String removeHead(String text) { + int startIndex = text.indexOf("<head>"); + int stopIndex = text.indexOf("</head>"); + if (startIndex == -1 || stopIndex == -1) return text; + stopIndex += 7; + return text.substring(0, startIndex) + text.substring(stopIndex); + } + + static String styleHeader = "<head></head>";/*<STYLE TYPE=\"text/css\"><!--\n" + + " h2 {\n" + + " font-size: x-large;\n" + + " font-family: Serif;\n" + + " font-weight: normal;\n" + + " }\n" + + " p {\n" + + " font-family: Serif;\n" + + " font-weight: normal;\n" + + //" color:black;\n" + + "}</head>\n";*/ + + + public static String applyProperties(String text, Map map) { + // ${name} -> map.get(name).toString() + int lastIndex = 0; + StringBuffer buf = new StringBuffer(); + + int startIndex; + while ( (startIndex = text.indexOf("${", lastIndex)) != -1) { + int endIndex = text.indexOf('}', startIndex); + //XXX internal error here + if (endIndex == -1) break; + buf.append(text.substring(lastIndex, startIndex)); + String key = text.substring(startIndex+2, endIndex); + lastIndex = endIndex+1; + Object replaceText = map.get(key); + //System.out.println("key: " + key + " -> " + replaceText); + if (replaceText == null) replaceText = "NOT_FOUND"; + buf.append(replaceText.toString()); + } + buf.append(text.substring(lastIndex)); + + return buf.toString(); + } + + public String applyProperties(String text) { + return applyProperties(text, context.getProperties()); + } + + protected String loadText(String filename) { + String fullname = Installer.RESOURCE_DIR + "/" + filename; + //context.installer.getPrefix() + "-" + filename; + + try { + String text = stringFromStream(getClass().getResourceAsStream(fullname)); + text = styleHeader + removeHead(text); + text = applyProperties(text); + //System.out.println(text); + return text; + } catch (IOException e) { + context.handleException(e); + return ""; + } + } + + protected JEditorPane makeHTMLArea(String filename) { + JEditorPane editorPane = new JEditorPane("text/html", loadText(filename)); + /* { + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D)g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + super.paint(g2); + } + };*/ + editorPane.setEditable(false); + editorPane.setOpaque(false); + return editorPane; + } + + protected void setHTMLArea(JEditorPane pane, String filename) { + pane.setText(loadText(filename)); + } + + + protected JPanel makeLocationBox(String label, JTextField textField, JButton browseButton) { + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS)); + + textField.setFont(context.getFont()); + textField.selectAll(); + box.add(textField); + + box.add(browseButton); + Border border = BorderFactory.createTitledBorder(label); + final int INSET = 8; + border = new CompoundBorder(border, new EmptyBorder(1, INSET, INSET, INSET)); + box.setBorder(border); + + return box; + } + + + private JPanel panel = null; + public JPanel getPanel() { + if (panel == null) { + panel = makePanel(); + panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + return panel; + } + + protected void setListener(JButton button, ActionListener listener) { + if (listener == null) { + button.setEnabled(false); + } else { + button.addActionListener(listener); + } + } + + protected Component makeButtons(Installer installer) { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + backButton = new JButton("Back"); + setListener(backButton, installer.makeBackAction(this)); + panel.add(backButton); + + nextButton = new JButton("Next"); + setListener(nextButton, installer.makeNextAction(this)); + panel.add(nextButton); //.setDefaultCapable(true); + + JLabel space = new JLabel(); + space.setPreferredSize(new Dimension(20, 0)); + panel.add(space); + + cancelButton = new JButton("Cancel"); + setListener(cancelButton, installer.makeCancelAction(this)); + panel.add(cancelButton); + + return panel; + } + + private Component buttons = null; + public Component getButtons() { + if (buttons == null) { + buttons = makeButtons(context.installer); + //buttons.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 10)); + } + context.installer.frame.getRootPane().setDefaultButton(nextButton); + return buttons; + } + + public void finish() { + if (Options.forceError2) { + throw new RuntimeException("forced error2 for testing purposes"); + } + } + public void run() {} +} + +class IntroPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("intro.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + return panel; + } +} + +class FinishPane extends WizardPane { + public JPanel makePanel() { + Component text = makeHTMLArea("finish.html"); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(text); + finalActions(); + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Finish"); + nextButton.setEnabled(true); + nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + cancelButton.setEnabled(false); + return ret; + } + + public void finalActions() { } +} + + +class LocationPane extends WizardPane implements ActionListener { + //XXX need more sophisticated default location finding + //XXX would like to find the place they last chose... + public String getDefaultLocation() { + if (context.onWindows()) { + //XXX hard-coded majorminor version needs to be fixed by 1.1 release + return "c:\\aspectj1.1"; + } else { + return new File(System.getProperty("user.home"), "aspectj1.1").getAbsolutePath(); + } + } + + protected JTextField location; + + public JPanel makePanel() { + Component text = makeHTMLArea("location.html"); + + location = new JTextField(getDefaultLocation()); + JButton browse = new JButton("Browse..."); + browse.addActionListener(this); + + JPanel locationBox = makeLocationBox("installation directory", location, browse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 1.0; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(locationBox, c); + panel.add(locationBox); + + //XXX set next button to read install + //context.nextButton.setText("Install"); + + return panel; + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + nextButton.setText("Install"); + return ret; + } + + public void actionPerformed(ActionEvent e) { + JFileChooser chooser = new JFileChooser(); // { + // public void approveSelection() { + // System.out.println("approved selection"); + // } + //}; //field.getText()); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int returnVal = chooser.showDialog(location, "Select"); + if(returnVal == JFileChooser.APPROVE_OPTION) { + File file = chooser.getSelectedFile(); + if (!file.isDirectory()) file = file.getParentFile(); + String name = file.getPath(); + location.setText(name); + location.selectAll(); + } + } + + /** + * Override to do any additional checks. + */ + protected void verify() {} + + public void finish() { + verify(); + context.setOutputDir(new File(location.getText())); + } +} + +class ConfigureLauncherPane extends WizardPane { + /* + //XXX check that the returned file is valid + public String getDefaultJavaLocation() { + String name = "java"; + if (context.onWindows()) name += ".exe"; + + if (Options.verbose) { + System.out.println("java.home: " + System.getProperty("java.home")); + System.out.println(" java: " + new File(new File(System.getProperty("java.home"), "bin"), name)); + System.out.println(" java: " + new File(new File(System.getProperty("java.home"), "bin"), name).getPath()); + } + + return new File(new File(System.getProperty("java.home"), "bin"), name).getPath(); + } + */ + + public String getDefaultJavaHomeLocation() { + if (!Options.forceHandConfigure) { + File javaHome = findJavaHome(); + if (javaHome != null) return javaHome.getPath(); + } + return null; + } + + public void chooseFile(JTextField field) { + JFileChooser chooser = new JFileChooser(); //field.getText()); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnVal = chooser.showDialog(field, "Select"); + if(returnVal == JFileChooser.APPROVE_OPTION) { + String name = chooser.getSelectedFile().getPath(); + field.setText(name); + field.selectAll(); + } + } + + + public ActionListener makeJavaLocationBrowseListener() { + return new ActionListener() { + public void actionPerformed(ActionEvent e) { + chooseFile(javaLocation); + } + }; + } + +// public ActionListener makeToolsJarLocationBrowseListener() { +// return new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// chooseFile(toolsJarLocation); +// } +// }; +// } + + + private JTextField javaLocation; + //private JTextField toolsJarLocation; + + public JPanel makePanel() { + String javaPath = getDefaultJavaHomeLocation(); + //String toolsJarPath = getDefaultToolsJarLocation(); + + Component text; + if (javaPath == null) { + javaPath = "<java home not found>"; + text = makeHTMLArea("configure-hand.html"); + } else { + text = makeHTMLArea("configure-auto.html"); + } + + javaLocation = new JTextField(javaPath); + JButton javaLocationBrowse = new JButton("Browse..."); + javaLocationBrowse.addActionListener(makeJavaLocationBrowseListener()); + + JPanel javaLocationBox = makeLocationBox("java home directory", javaLocation, javaLocationBrowse); + + +// toolsJarLocation = new JTextField(toolsJarPath); +// JButton toolsJarLocationBrowse = new JButton("Browse..."); +// toolsJarLocationBrowse.addActionListener(makeToolsJarLocationBrowseListener()); + +// JPanel toolsJarLocationBox = makeLocationBox("full path to tools.jar", toolsJarLocation, toolsJarLocationBrowse); + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(text, c); + panel.add(text); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(javaLocationBox, c); + panel.add(javaLocationBox); + +// c.weighty = 0.25; +// JLabel space = new JLabel(); +// bag.setConstraints(space, c); +// panel.add(space); + +// c.weighty = 0.0; +// bag.setConstraints(toolsJarLocationBox, c); +// panel.add(toolsJarLocationBox); + + c.weighty = 0.5; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + public void finish() { + context.javaPath = new File(javaLocation.getText()); +// context.toolsJarPath = new File(toolsJarLocation.getText()); + + //XXX need much more work on helping the user get these paths right +// if (context.javaPath.isDirectory()) { +// context.javaPath = new File(context.javaPath, "java"); +// } +// if (context.toolsJarPath.isDirectory()) { +// context.toolsJarPath = new File(context.toolsJarPath, "tools.jar"); +// } + } + + + //XXX add user.home to prefixes in a rational way + public static final String[] windowsPaths = { + "c:\\jdk", "c:\\apps\\jdk", "${user.home}\\jdk" + }; + + public static final String[] unixPaths = { + "/usr/local/bin/jdk", "/usr/bin/jdk", "/usr/bin/jdk", "${user.home}/jdk" + }; + + public static final String[] suffixes = { + "1.3.1", "1.3", "1.2", "13", "12", "2", "", "1.4" + }; + + public static boolean windows = true; + + public boolean isLegalJavaHome(File home) { + File bin = new File(home, "bin"); + return new File(bin, "java").isFile() || new File(bin, "java.exe").isFile(); + } + + public boolean isLegalJDKHome(File home) { + File lib = new File(home, "lib"); + return new File(lib, "tools.jar").isFile(); + } + + + public File findJavaHome() { + String s = System.getProperty("java.home"); + File javaHome = null; + if (s != null) { + javaHome = new File(s); + if (isLegalJDKHome(javaHome)) return javaHome; + if (isLegalJavaHome(javaHome)) { + File parent = javaHome.getParentFile(); + if (parent != null && isLegalJDKHome(parent)) return parent; + } + } + + String[] paths; + if (windows) { + paths = windowsPaths; + } else { + paths = unixPaths; + } + + for (int suffixIndex = 0; suffixIndex < suffixes.length; suffixIndex++) { + String suffix = suffixes[suffixIndex]; + for (int prefixIndex = 0; prefixIndex < paths.length; prefixIndex++) { + String prefix = paths[prefixIndex]; + prefix = applyProperties(prefix); + File test = new File(prefix+suffix); + if (isLegalJavaHome(test)) { + if (isLegalJDKHome(test)) return test; + else if (javaHome == null) javaHome = test; + } + } + } + return javaHome; + } +} + + +class InstallPane extends WizardPane { + private JProgressBar progressBar; + private JTextField progressItem; + private JEditorPane message; + + private boolean makeLaunchScripts = false; + + public InstallPane(boolean makeLaunchScripts) { + this.makeLaunchScripts = makeLaunchScripts; + } + + public JPanel makePanel() { + message = makeHTMLArea("install-start.html"); + + progressBar = new JProgressBar(); + + progressItem = new JTextField(); + progressItem.setOpaque(false); + progressItem.setFont(context.getFont()); + progressItem.setEditable(false); + + + GridBagLayout bag = new GridBagLayout(); + GridBagConstraints c = new GridBagConstraints(); + + JPanel panel = new JPanel(bag); + c.fill = GridBagConstraints.BOTH; + c.weightx = 1.0; + c.weighty = 1.0; + //c.ipady = 10; + c.gridwidth = GridBagConstraints.REMAINDER; + bag.setConstraints(message, c); + panel.add(message); + + c.weighty = 0.0; + //c.fill = GridBagConstraints.VERTICAL; + bag.setConstraints(progressBar, c); + panel.add(progressBar); + + c.weighty = 0.1; + JLabel space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + c.weighty = 0.0; + bag.setConstraints(progressItem, c); + panel.add(progressItem); + + c.weighty = 0.5; + space = new JLabel(); + bag.setConstraints(space, c); + panel.add(space); + + return panel; + } + + class InstallRunner implements Runnable { + public InstallRunner() {} + + public void run() { + try { + new CurrentJarUnpacker(context, InstallPane.this).unpack(Installer.RESOURCE_DIR + "/contents.txt", context.getOutputDir()); + + if (makeLaunchScripts) { + LaunchScriptMaker lsm = new LaunchScriptMaker(context); + lsm.writeScript("ajc"); + //lsm.writeScript("ajdoc"); + //lsm.writeScript("ajdb"); + lsm.writeScript("ajbrowser"); + } + if (hasGui()) { + progressBar.setValue(100); + setHTMLArea(message, "install-finish.html"); + } + } catch (IOException ioe) { + context.handleException(ioe); + } + + if (hasGui()) { + cancelButton.setEnabled(false); + nextButton.setEnabled(true); + } + } + } + + public Component makeButtons(Installer installer) { + Component ret = super.makeButtons(installer); + //nextButton.setText("Finish"); + nextButton.setEnabled(false); + //nextButton.addActionListener(installer.makeFinishAction(this)); + backButton.setEnabled(false); + return ret; + } + + + public void run() { + Thread thread = new Thread(new InstallRunner()); + thread.start(); + } + + public void progressMessage(final String message) { + if (!hasGui()) { + return; + } + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressItem.setText(message); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } + + int nBytes = 0; + int bytesWritten = 0; + public void progressBytesWritten(int bytes) { + if (!hasGui()) { + return; + } + bytesWritten += bytes; + final int PCT = (int)(100.0 * bytesWritten / nBytes); + //System.out.println("bytesWritten: " + bytesWritten); + try { + //XXX performance tradeoff between invokeAndWait and invokeLater... + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + progressBar.setValue(PCT); + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + } +} + +class CurrentJarUnpacker { + InstallContext context; + InstallPane installPane; + + public CurrentJarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ( (index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(InputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) return; + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(InputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ( (nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(InputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ( (l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeResource(String name, File outputDir) throws IOException { + File outputFile = makeOutputFile(name, outputDir); + //System.out.println("finding name: " + name); + writeStream(getClass().getResourceAsStream("/" + name), outputFile); + } + + public void unpack(String contentsName, File outputDir) throws IOException { + URL url = getClass().getResource(contentsName); + InputStream stream = url.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "US-ASCII")); + + String line = reader.readLine(); + installPane.nBytes = Integer.parseInt(line); + + while ( (line = reader.readLine()) != null) { + writeResource(line, outputDir); + } + + installPane.progressMessage("done writing"); + } +} + +class LaunchScriptMaker { + static final String toolsPackage = "org.aspectj.tools"; + + InstallContext context; + public LaunchScriptMaker(InstallContext context) { + this.context = context; + } + + private void writeWindowsHeader(String className, PrintStream ps) { + ps.println("@echo off"); + ps.println("REM This file generated by AspectJ installer"); + ps.println("REM Created on "+new java.util.Date()+" by "+ + System.getProperty("user.name")); + ps.println(""); + ps.println("if \"%JAVA_HOME%\" == \"\" set JAVA_HOME=" + + context.javaPath.getAbsolutePath()); + ps.println("if \"%ASPECTJ_HOME%\" == \"\" set ASPECTJ_HOME=" + + context.getOutputDir().getAbsolutePath()); + ps.println(""); + + ps.println("if exist \"%JAVA_HOME%\\bin\\java.exe\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java.bat\" goto haveJava"); + ps.println("if exist \"%JAVA_HOME%\\bin\\java\" goto haveJava"); + + ps.println("echo java does not exist as %JAVA_HOME%\\bin\\java"); + ps.println("echo please fix the JAVA_HOME environment variable"); + ps.println(":haveJava"); + ps.println("\"%JAVA_HOME%\\bin\\java\" -classpath " + + "\"%ASPECTJ_HOME%\\lib\\aspectjtools.jar;%JAVA_HOME%\\lib\\tools.jar;%CLASSPATH%\""+ + " -Xmx64M " + className + //" -defaultClasspath " + "\"%CLASSPATH%\"" + + " " + makeScriptArgs(false)); + } + + private void writeUnixHeader(String className, PrintStream ps) { + File binsh = new File(File.separator+"bin", "sh"); + if (binsh.canRead()) { + ps.println("#!"+binsh.getPath()); + } + ps.println("# This file generated by AspectJ installer"); + ps.println("# Created on "+new java.util.Date()+" by "+ + System.getProperty("user.name")); + ps.println(""); + ps.println("if [ \"$JAVA_HOME\" = \"\" ] ; then JAVA_HOME=" + + quote(true, false, context.javaPath.getAbsolutePath())); + ps.println("fi"); + ps.println("if [ \"$ASPECTJ_HOME\" = \"\" ] ; then ASPECTJ_HOME=" + + quote(true, false, context.getOutputDir())); + ps.println("fi"); + ps.println(""); + String sep = File.pathSeparator; + + ps.println("\"$JAVA_HOME/bin/java\" -classpath "+ + "\"$ASPECTJ_HOME/lib/aspectjtools.jar" + sep + + "$JAVA_HOME/lib/tools.jar" + sep + + "$CLASSPATH\""+ + " -Xmx64M " + className + + " " + makeScriptArgs(true)); + } + + + private void makeExecutable(File file) { + try { + Runtime curRuntime = Runtime.getRuntime(); + curRuntime.exec("chmod 777 " + quote(true, false, file)); + } catch (Throwable t) { + // ignore any errors that occur while trying to chmod + } + } + + + + private String makeScriptArgs(boolean unixStyle) { + if (unixStyle) { + return "\"$@\""; + } else if (context.onWindowsPro()) { + return "%*"; + } else { + return "%1 %2 %3 %4 %5 %6 %7 %8 %9"; + } + } + + private String quote(boolean unixStyle, boolean forceQuotes, File file) { + return quote(unixStyle, forceQuotes, file.getAbsolutePath()); + } + private String quote(boolean unixStyle, boolean forceQuotes, String s) { + if (context.onWindows() && unixStyle) { + s = s.replace('\\', '/'); + } + + if (!forceQuotes && s.indexOf(' ') == -1) return s; + return "\""+s+"\""; + } + + private File makeScriptFile(String name, boolean unixStyle) throws IOException { + if (!unixStyle) { + if (context.onOS2()) { + name += ".cmd"; + } else if (context.onWindows()) { + name += ".bat"; + } + } + + //XXX probably want a context.getOutputBinDir() + File bindir = new File(context.getOutputDir(), "bin"); + bindir.mkdirs(); + File file = new File(bindir, name); + return file; + } + + private PrintStream getPrintStream(File file) throws IOException { + return new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); + } + + String makeClassPathVar(boolean unixStyle) { + if (unixStyle) { + return "$CLASSPATH"; + } else { + return "%CLASSPATH%"; + } + } + + public String makeClassPath(boolean unixStyle) throws IOException { + return context.toolsJarPath.getAbsolutePath() + File.pathSeparator + + //XXX want context.getOutputLibDir() + new File(new File(context.getOutputDir(), "lib"), "aspectjtools.jar").getAbsolutePath() + + File.pathSeparator + makeClassPathVar(unixStyle); + } + + public void writeScript(String className, PrintStream ps, boolean unixStyle) throws IOException { + if (unixStyle) writeUnixHeader(className, ps); + else writeWindowsHeader(className, ps); + + /* + ps.print(quote(unixStyle, false, context.javaPath.getAbsolutePath())); + ps.print(" "); + ps.print("-classpath "); + ps.print(quote(unixStyle, true, makeClassPath(unixStyle))); + ps.print(" "); + ps.print("-Xmx64M "); + ps.print(className); + ps.print(" "); + ps.print(makeScriptArgs(unixStyle)); + */ + } + + public void writeScript(String className, boolean unixStyle) throws IOException { + File file = makeScriptFile(className, unixStyle); + if (!checkExistingFile(file)) return; + PrintStream ps = getPrintStream(file); + writeScript(toolsPackage + '.' + className + ".Main", ps, unixStyle); + ps.close(); + //??? unixStyle vs. onUnix() + if (context.onUnix()) makeExecutable(file); + } + + public boolean checkExistingFile(File file) { + if (!file.exists()) return true; + + return context.shouldOverwrite(file); + } + + /* + final static String OVERWRITE_MESSAGE = "Overwrite launch script "; + final static String OVERWRITE_TITLE = "Overwrite?"; + + final static String[] OVERWRITE_OPTIONS = { + "Yes", "No", "Yes to all", "No to all" + }; + + final static int OVERWRITE_YES = 0; + final static int OVERWRITE_NO = 1; + final static int OVERWRITE_ALL = 2; + final static int OVERWRITE_NONE = 3; + + int overwriteState = OVERWRITE_NO; + boolean shouldOverwrite(final File file) { + if (overwriteState == OVERWRITE_ALL) return true; + if (overwriteState == OVERWRITE_NONE) return false; + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + int ret = JOptionPane.showOptionDialog(context.installer.frame, + OVERWRITE_MESSAGE+file.getPath(), OVERWRITE_TITLE, + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, + OVERWRITE_OPTIONS, OVERWRITE_OPTIONS[OVERWRITE_YES]); + + overwriteState = ret; + } + }); + } catch (InvocationTargetException ite) { + context.handleException(ite.getTargetException()); + } catch (InterruptedException ie) { + } + + return overwriteState == OVERWRITE_YES || overwriteState == OVERWRITE_ALL; + } + */ + + + public void writeScript(String className) throws IOException { + writeScript(className, true); + if (context.onWindows()) { + writeScript(className, false); + } + } +} + + +class JarUnpacker { + InstallContext context; + InstallPane installPane; + + public JarUnpacker(InstallContext context, InstallPane installPane) { + this.context = context; + this.installPane = installPane; + } + + public File makeOutputFile(String name, File outputFile) { + int index; + int lastIndex = 0; + + while ( (index = name.indexOf('/', lastIndex)) != -1) { + outputFile = new File(outputFile, name.substring(lastIndex, index)); + lastIndex = index + 1; + } + + return new File(outputFile, name.substring(lastIndex)); + } + + final static int BUF_SIZE = 4096; + + public void writeStream(ZipInputStream zis, File outputFile) throws IOException { + if (outputFile.exists()) { + if (!context.shouldOverwrite(outputFile)) return; + } + + installPane.progressMessage("writing " + outputFile.getAbsolutePath()); + + outputFile.getParentFile().mkdirs(); + + if (context.isTextFile(outputFile)) { + writeTextStream(zis, outputFile); + } else { + writeBinaryStream(zis, outputFile); + } + } + + public void writeBinaryStream(ZipInputStream zis, File outputFile) throws IOException { + byte[] buffer = new byte[BUF_SIZE]; + int nRead = 0; + + OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); + + while ( (nRead = zis.read(buffer)) != -1) { + os.write(buffer, 0, nRead); + installPane.progressBytesWritten(nRead); + } + os.close(); + } + + public void writeTextStream(ZipInputStream zis, File outputFile) throws IOException { + BufferedWriter os = new BufferedWriter(new FileWriter(outputFile)); + BufferedReader r = new BufferedReader(new InputStreamReader(zis, "US-ASCII")); + + String l; + while ( (l = r.readLine()) != null) { + os.write(l); + os.newLine(); + installPane.progressBytesWritten(l.length() + 1); + } + os.close(); + } + + public void writeEntry(ZipInputStream zis, ZipEntry entry, File outputDir) throws IOException { + if (entry.isDirectory()) return; + + String name = entry.getName(); + File outputFile = makeOutputFile(name, outputDir); + writeStream(zis, outputFile); + } + + public void unpack(String jarName, File outputDir) throws IOException { + URL url = getClass().getResource(jarName); + InputStream stream = url.openStream(); + ZipInputStream zis = new ZipInputStream(stream); + int i = 0; + + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + final String name = entry.getName(); + writeEntry(zis, entry, outputDir); + // + } + installPane.progressMessage("done writing"); + } +} + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java new file mode 100644 index 000000000..4dd35c4b8 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJInstaller.java @@ -0,0 +1,330 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +//XXX INCLUDES CODE FROM ANT -- UNDER APACHE LICENSE +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.types.*; +import java.io.*; +import java.util.zip.ZipOutputStream; +import java.util.*; +import java.util.zip.*; +import org.apache.tools.ant.taskdefs.*; +import org.apache.tools.ant.*; + +public class AJInstaller extends MatchingTask { + static final String INCLUDE_CLASSES = "$installer$/org/aspectj/*.class"; + static final String MAIN_CLASS = "$installer$.org.aspectj.Main"; + static final String CONTENTS_FILE = "$installer$/org/aspectj/resources/contents.txt"; + private String htmlSrc; + + public void setHtmlSrc(String v) { htmlSrc = v; } + + private String resourcesSrc; + + public void setResourcesSrc(String v) { resourcesSrc = v; } + + private String mainclass; + + public void setMainclass(String v) { mainclass = v; } + + private File installerClassJar; + + public void setInstallerclassjar(String v) { + installerClassJar = project.resolveFile(v); + } + + protected List contentsNames = new ArrayList(); + + protected long contentsBytes = 0; + + protected void addToContents(File file, String vPath) { + contentsNames.add(vPath); + contentsBytes += file.length(); + } + + String[] getFiles(File baseDir) { + DirectoryScanner ds = new DirectoryScanner(); + setBasedir(baseDir.getAbsolutePath()); + ds.setBasedir(baseDir); + //ds.setIncludes(new String [] {pattern}); + ds.scan(); + return ds.getIncludedFiles(); + } + + protected Copy getCopyTask() { + Copy cd = (Copy)project.createTask("copy"); + if (null == cd) { + log("project.createTask(\"copy\") failed - direct", project.MSG_VERBOSE); + cd = new Copy(); + cd.setProject(getProject()); + } + return cd; + } + protected void finishZipOutputStream(ZipOutputStream zOut) throws IOException, BuildException { + writeContents(zOut); + writeManifest(zOut); + File tmpDirF = File.createTempFile("tgz", ".di"); + File tmpDir = new File(tmpDirF.getAbsolutePath() + "r"); + tmpDirF.delete(); + String tmp = tmpDir.getAbsolutePath(); + + // installer class files + Expand expand = new Expand(); + expand.setProject(getProject()); + expand.setSrc(installerClassJar); + expand.setDest(new File(tmp)); + PatternSet patterns = new PatternSet(); + patterns.setIncludes(INCLUDE_CLASSES); + expand.addPatternset(patterns); + expand.execute(); + + // move the correct resource files into the jar + Copy cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("*"); + fileset.setExcludes("contents.txt,properties.txt"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + project.addFilter("installer.main.class", this.mainclass); + Copy cf = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(resourcesSrc)); + fileset.setIncludes("properties.txt"); + cf.setFiltering(true); + cf.addFileset(fileset); + cf.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cf.execute(); + // move the correct resource files into the jar + cd = getCopyTask(); + fileset = new FileSet(); + fileset.setDir(new File(htmlSrc)); + fileset.setIncludes("*"); + cd.addFileset(fileset); + cd.setTodir(new File(tmp+"/$installer$/org/aspectj/resources")); + cd.execute(); + // now move these files into the jar + setBasedir(tmp); + writeFiles(zOut, getFiles(tmpDir)); + // and delete the tmp dir + Delete dt = (Delete)project.createTask("delete"); + if (null == dt) { + dt = new Delete(); + dt.setProject(getProject()); + } + dt.setDir(new File(tmp)); + dt.execute(); + } + + static final char NEWLINE = '\n'; + + protected void writeContents(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append(contentsBytes); + buf.append(NEWLINE); + for (Iterator i = contentsNames.iterator(); i.hasNext(); ) { + String name = (String)i.next(); + buf.append(name); + buf.append(NEWLINE); + } + zipFile(new StringBufferInputStream(buf.toString()), zOut, CONTENTS_FILE, System.currentTimeMillis()); + } + + protected void writeManifest(ZipOutputStream zOut) throws IOException { + // write to a StringBuffer + StringBuffer buf = new StringBuffer(); + buf.append("Manifest-Version: 1.0"); + buf.append(NEWLINE); + buf.append("Main-Class: " + MAIN_CLASS); + buf.append(NEWLINE); + zipFile(new StringBufferInputStream(buf.toString()), zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis()); + } + + //XXX cut-and-paste from Zip super-class (under apache license) + private File zipFile; + private File baseDir; + private boolean doCompress = true; + protected String archiveType = "zip"; + + /** + * This is the name/location of where to + * create the .zip file. + */ + public void setZipfile(String zipFilename) { + zipFile = project.resolveFile(zipFilename); + } + + /** + * This is the base directory to look in for + * things to zip. + */ + public void setBasedir(String baseDirname) { + baseDir = project.resolveFile(baseDirname); + } + + /** + * Sets whether we want to compress the files or only store them. + */ + public void setCompress(String compress) { + doCompress = Project.toBoolean(compress); + } + + protected void initZipOutputStream(ZipOutputStream zOut) + throws IOException, BuildException + { + } + + protected void zipDir(File dir, ZipOutputStream zOut, String vPath) + throws IOException + { + } + + protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath, + long lastModified) + throws IOException + { + ZipEntry ze = new ZipEntry(vPath); + ze.setTime(lastModified); + + /* + * XXX ZipOutputStream.putEntry expects the ZipEntry to know its + * size and the CRC sum before you start writing the data when using + * STORED mode. + * + * This forces us to process the data twice. + * + * I couldn't find any documentation on this, just found out by try + * and error. + */ + if (!doCompress) { + long size = 0; + CRC32 cal = new CRC32(); + if (!in.markSupported()) { + // Store data into a byte[] + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + bos.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in = new ByteArrayInputStream(bos.toByteArray()); + } else { + in.mark(Integer.MAX_VALUE); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + size += count; + cal.update(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + in.reset(); + } + ze.setSize(size); + ze.setCrc(cal.getValue()); + } + zOut.putNextEntry(ze); + byte[] buffer = new byte[8 * 1024]; + int count = 0; + do { + zOut.write(buffer, 0, count); + count = in.read(buffer, 0, buffer.length); + } while (count != -1); + } + + protected void zipFile(File file, ZipOutputStream zOut, String vPath) + throws IOException + { + if ( !vPath.startsWith("$installer$") ) { + addToContents(file, vPath); + } + FileInputStream fIn = new FileInputStream(file); + try { + zipFile(fIn, zOut, vPath, file.lastModified()); + } finally { + fIn.close(); + } + } + + public void execute() throws BuildException { + if (installerClassJar == null) { + throw new BuildException("installerClassJar attribute must be set!"); + } + if (!installerClassJar.canRead() + || !installerClassJar.getPath().endsWith(".jar")) { + throw new BuildException("not readable jar:" + installerClassJar); + } +// if (installerClassDir == null) { +// throw new BuildException("installerClassDir attribute must be set!"); +// } +// if (!installerClassDir.exists()) { +// throw new BuildException("no such directory: installerClassDir=" + installerClassDir); +// } + if (baseDir == null) { + throw new BuildException("basedir attribute must be set!"); + } + if (!baseDir.exists()) { + throw new BuildException("basedir does not exist!"); + } + DirectoryScanner ds = super.getDirectoryScanner(baseDir); + String[] files = ds.getIncludedFiles(); + String[] dirs = ds.getIncludedDirectories(); + log("Building installer: "+ zipFile.getAbsolutePath()); + ZipOutputStream zOut = null; + try { + zOut = new ZipOutputStream(new FileOutputStream(zipFile)); + if (doCompress) { + zOut.setMethod(ZipOutputStream.DEFLATED); + } else { + zOut.setMethod(ZipOutputStream.STORED); + } + initZipOutputStream(zOut); + writeDirs(zOut, dirs); + writeFiles(zOut, files); + finishZipOutputStream(zOut); + } catch (IOException ioe) { + String msg = "Problem creating " + archiveType + " " + ioe.getMessage(); + throw new BuildException(msg, ioe, location); + } finally { + if (zOut != null) { + try { + // close up + zOut.close(); + } + catch (IOException e) {} + } + } + } + + protected void writeDirs(ZipOutputStream zOut, String[] dirs) throws IOException { + for (int i = 0; i < dirs.length; i++) { + File f = new File(baseDir,dirs[i]); + String name = dirs[i].replace(File.separatorChar,'/')+"/"; + zipDir(f, zOut, name); + } + } + + protected void writeFiles(ZipOutputStream zOut, String[] files) throws IOException { + for (int i = 0; i < files.length; i++) { + File f = new File(baseDir,files[i]); + String name = files[i].replace(File.separatorChar,'/'); + zipFile(f, zOut, name); + } + } + +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java new file mode 100644 index 000000000..4a778cb67 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AJPush.java @@ -0,0 +1,89 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; +import java.util.*; +import java.text.DecimalFormat; + +public class AJPush extends ConditionalTask { + private File src; + + public void setSrc(String v) { src = project.resolveFile(v); } + + private String key; + + public void setKey(String v) { key = v; } + + File releaseDir = null; + File downloadDir = null; + boolean waiting = false; + + public void execute() throws org.apache.tools.ant.BuildException { + //File releaseDir = src.getParentFile(); + // todo: dependency on ant script variable name aj.release.dir + releaseDir = project.resolveFile(project.getProperty("aj.release.dir")); + // todo: dependency on ant script variable name download.dir + downloadDir = project.resolveFile(project.getProperty("download.dir")); + // For testing make sure these directories are made + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(releaseDir); + mkdir.execute(); + mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(downloadDir); + mkdir.execute(); + log("Pushing from " + releaseDir + " to " + downloadDir); + // add info to release.txt + try { + File releaseFile = new File(releaseDir, "release.txt"); + File downloadFile = new File(downloadDir, "release.txt"); + if (!releaseFile.canRead()) { + releaseFile.createNewFile(); + } + addReleaseInfo(src, releaseFile); + // copy to staging web server + project.copyFile(src, new File(downloadDir, src.getName())); + project.copyFile(releaseFile, downloadFile); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + void addReleaseInfo(File file, File propFile) throws IOException { + Properties props = new Properties(); + if (propFile.canRead()) { + props.load(new FileInputStream(propFile)); + } + file.createNewFile(); // create new only if necessary + long bytes = file.length(); + DecimalFormat df = new DecimalFormat(); + df.setGroupingSize(3); + String bytesString = df.format(bytes); + props.put("release." + key + ".size.bytes", bytesString); + props.put("release." + key + ".date", project.getProperty("build.date")); + props.put("release." + key + ".filename", file.getName()); + props.put("release.date", project.getProperty("build.date")); + props.put("release.version", project.getProperty("build.version.short")); + props.put("release.versionName", project.getProperty("build.version.long")); + String userName = System.getProperty("user.name"); + if (userName != null) { + props.put("release." + key + ".username", userName); + } + props.store(new FileOutputStream(propFile), null); + } + +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java new file mode 100644 index 000000000..d4001161e --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/AntBuilder.java @@ -0,0 +1,721 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Copy; +import org.apache.tools.ant.taskdefs.Filter; +import org.apache.tools.ant.taskdefs.Javac; +import org.apache.tools.ant.taskdefs.Zip; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.ZipFileSet; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; +import org.aspectj.internal.tools.build.Messager; +import org.aspectj.internal.tools.build.Module; +import org.aspectj.internal.tools.build.Modules; +import org.aspectj.internal.tools.build.ProductModule; +import org.aspectj.internal.tools.build.Util; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +/** + * Implement Builder in Ant. + */ +public class AntBuilder extends Builder { + /* + * XXX This just constructs and uses Ant Task objects, + * which in some cases causes the tasks to fail. + */ + + /** @return a Builder for this project and configuration */ + public static Builder getBuilder(String config, Project project, File tempDir) { + boolean useEclipseCompiles = false; + boolean verbose = false; + if (null != config) { + if (-1 != config.indexOf("useEclipseCompiles")) { + useEclipseCompiles = true; + } + if (-1 != config.indexOf("verbose")) { + verbose = true; + } + } + Messager handler = new ProjectMessager(project); + Builder result = new ProductBuilder(project, tempDir, useEclipseCompiles, handler); + if (verbose) { + result.setVerbose(true); + } + return result; + } + + /** + * Make and register target for this module and antecedants. + * This ensures that the (direct) depends list is generated + * for each target. + * This depends on topoSort to detect cycles. XXX unverified + */ + private static void makeTargetsForModule( + final Module module, + final Hashtable targets, + final boolean rebuild) { + Target target = (Target) targets.get(module.name); + if (null == target) { + // first add the target + target = new Target(); + target.setName(module.name); + List req = module.getRequired(); + StringBuffer depends = new StringBuffer(); + boolean first = true; + for (Iterator iterator = req.iterator(); iterator.hasNext();) { + Module reqModule = (Module) iterator.next(); + if (rebuild || reqModule.outOfDate(false)) { + if (!first) { + depends.append(","); + } else { + first = false; + } + depends.append(reqModule.name); + } + } + if (0 < depends.length()) { + target.setDepends(depends.toString()); + } + targets.put(module.name, target); + + // then recursively add any required modules + for (Iterator iterator = module.getRequired().iterator(); + iterator.hasNext(); + ) { + Module reqModule = (Module) iterator.next(); + if (rebuild || reqModule.outOfDate(false)) { + makeTargetsForModule(reqModule, targets, rebuild); + } + } + } + } + + protected final Project project; + + protected AntBuilder(Project project, File tempDir, boolean useEclipseCompiles, + Messager handler) { + super(tempDir, useEclipseCompiles, handler); + this.project = project; + Util.iaxIfNull(project, "project"); + } + protected boolean copyFile(File fromFile, File toFile, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setFile(fromFile); + copy.setTofile(toFile); + executeTask(copy); + return true; + } + + protected void copyFileset(File toDir, FileSet fileSet, boolean filter) { + Copy copy = makeCopyTask(filter); + copy.addFileset(fileSet); + copy.setTodir(toDir); + executeTask(copy); + } + + /** filter if FILTER_ON, use filters */ + protected Copy makeCopyTask(boolean filter) { + Copy copy = new Copy(); + copy.setProject(project); + if (FILTER_ON == filter) { + copy.setFiltering(true); + setupFilters(); + } + return copy; + } + + /** lazily and manually generate properties */ + protected Properties getFilterProperties() { // + if (filterProps == null) { + long time = System.currentTimeMillis(); + String version = null; // unknown - system? + filterProps = BuildSpec.getFilterProperties(time, version); + } + return filterProps; + } + + protected void setupFilters() { + if (!filterSetup) { // XXX check Ant - is this static? + Properties props = getFilterProperties(); + for (Enumeration enum = props.keys(); enum.hasMoreElements();) { + String token = (String) enum.nextElement(); + String value = props.getProperty(token); + Filter filter = new Filter(); + filter.setProject(project); + filter.setToken(token); + filter.setValue(value); + if (!executeTask(filter)) { + return; + } + } + filterSetup = true; + } + } + + protected boolean compile(Module module, File classesDir, List errors) { + // XXX test whether build.compiler property takes effect automatically + // I suspect it requires the proper adapter setup. + Javac javac = new Javac(); + javac.setProject(project); + + // -- source paths + Path path = new Path(project); + for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + path.createPathElement().setLocation(file); + } + javac.setSrcdir(path); + path = null; + + // -- classpath + Path classpath = new Path(project); + boolean hasLibraries = false; + // modules + for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + classpath.createPathElement().setLocation(required.getModuleJar()); + if (!hasLibraries) { + hasLibraries = true; + } + } + // libraries + for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + classpath.createPathElement().setLocation(file); + if (!hasLibraries) { + hasLibraries = true; + } + } + // need to add system classes?? + boolean inEclipse = true; // XXX detect, fork only in eclipse + if (hasLibraries && inEclipse) { + javac.setFork(true); // XXX otherwise never releases library jars + } + + // -- set output directory + classpath.createPathElement().setLocation(classesDir); + javac.setClasspath(classpath); + javac.setDestdir(classesDir); + if (!classesDir.mkdirs()) { + errors.add("unable to create classes directory"); + return false; + } + + // compile + try { + return executeTask(javac); + } catch (BuildException e) { + String args = "" + Arrays.asList(javac.getCurrentCompilerArgs()); + errors.add("BuildException compiling " + module.toLongString() + args + + ": " + Util.renderException(e)); + return false; + } finally { + javac.init(); // be nice to let go of classpath libraries... + } + } + + /** + * Merge classes directory and any merge jars into module jar + * with any specified manifest file. + * META-INF directories are excluded. + */ + protected boolean assemble(Module module, File classesDir, List errors) { + if (!buildingEnabled) { + return false; + } + // ---- zip result up + Zip zip = new Zip(); + zip.setProject(project); + zip.setDestFile(module.getModuleJar()); + ZipFileSet zipfileset = null; + + // -- merge any resources in any of the src directories + for (Iterator iter = module.getSrcDirs().iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setDir(srcDir); + zipfileset.setIncludes(RESOURCE_PATTERN); + zip.addZipfileset(zipfileset); + } + + // -- merge any merge jars + List mergeJars = module.getMerges(); + final boolean useManifest = false; + if (0 < mergeJars.size()) { + for (Iterator iter = mergeJars.iterator(); iter.hasNext();) { + File mergeJar = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(mergeJar); + zipfileset.setIncludes("**/*"); + zipfileset.setExcludes("META-INF/*"); // XXXFileLiteral + zipfileset.setExcludes("meta-inf/*"); + zip.addZipfileset(zipfileset); + } + } + // merge classes; put any meta-inf/manifest.mf here + File metaInfDir = new File(classesDir, "META-INF"); + if (metaInfDir.canWrite()) { + Util.deleteContents(metaInfDir); // XXX only delete manifest + } + + // -- manifest + File manifest = new File(module.moduleDir, module.name + ".mf.txt"); // XXXFileLiteral + if (Util.canReadFile(manifest)) { + if (Util.canReadDir(metaInfDir) || metaInfDir.mkdirs()) { + copyFile(manifest, new File(metaInfDir, "manifest.mf"), FILTER_ON); // XXXFileLiteral + } else { + errors.add("have manifest, but unable to create " + metaInfDir); + return false; + } + } + + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setDir(classesDir); + zipfileset.setIncludes("**/*"); + zip.addZipfileset(zipfileset); + + try { + handler.log("assembling " + module + " in " + module.getModuleJar()); + return executeTask(zip); + } catch (BuildException e) { + errors.add("BuildException zipping " + module + ": " + e.getMessage()); + return false; + } finally { + module.clearOutOfDate(); + } + } + /** + * @see org.aspectj.internal.tools.build.Builder#buildAntecedants(Module) + */ + protected String[] getAntecedantModuleNames(Module module, boolean rebuild) { + Hashtable targets = new Hashtable(); + makeTargetsForModule(module, targets, rebuild); + // XXX bug: doc says topoSort returns String, but returns Target + Collection result = project.topoSort(module.name, targets); + // XXX is it topoSort that should detect cycles? + int size = result.size(); + if (0 == result.size()) { + return new String[0]; + } + ArrayList toReturn = new ArrayList(); + for (Iterator iter = result.iterator(); iter.hasNext();) { + Target target = (Target) iter.next(); + String name = target.getName(); + if (null == name) { + throw new Error("null name?"); + } else { + toReturn.add(name); + } + } + // topoSort always returns module.name + if ((1 == size) + && module.name.equals(toReturn.get(0)) + && !module.outOfDate(false)) { + return new String[0]; + } + return (String[]) toReturn.toArray(new String[0]); + } + + /** + * Generate Module.assembledJar with merge of itself and all antecedants + */ + protected boolean assembleAll(Module module, Messager handler) { + if (!buildingEnabled) { + return false; + } + Util.iaxIfNull(module, "module"); + Util.iaxIfNull(handler, "handler"); + if (module.outOfDate(false)) { + throw new IllegalStateException("module out of date: " + module); + } + + // ---- zip result up + Zip zip = new Zip(); + zip.setProject(project); + zip.setDestFile(module.getAssembledJar()); + ZipFileSet zipfileset = null; + + ArrayList known = module.findKnownJarAntecedants(); + + // -- merge any antecedents, less any manifest + for (Iterator iter = known.iterator(); iter.hasNext();) { + File jarFile = (File) iter.next(); + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(jarFile); + zipfileset.setIncludes("**/*"); + zipfileset.setExcludes("META-INF/MANIFEST.MF"); // XXXFileLiteral + zipfileset.setExcludes("META-INF/manifest.mf"); + zipfileset.setExcludes("meta-inf/manifest.mf"); + zipfileset.setExcludes("meta-inf/MANIFEST.MF"); + zip.addZipfileset(zipfileset); + } + + // merge the module jar itself, including same manifest (?) + zipfileset = new ZipFileSet(); + zipfileset.setProject(project); + zipfileset.setSrc(module.getModuleJar()); + zip.addZipfileset(zipfileset); + + try { + handler.log("assembling all " + module + " in " + module.getAssembledJar()); + return executeTask(zip); + } catch (BuildException e) { + handler.logException("BuildException zipping " + module, e); + return false; + } finally { + module.clearOutOfDate(); + } + } + + /** + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#buildInstaller(BuildSpec, String) + */ + protected boolean buildInstaller( + BuildSpec buildSpec, + String targDirPath) { + return false; + } + + /** + * @see org.aspectj.internal.tools.ant.taskdefs.Builder#copyFiles(File, File, String, String, boolean) + */ + protected boolean copyFiles( + File fromDir, + File toDir, + String includes, + String excludes, + boolean filter) { + Copy copy = makeCopyTask(filter); + copy.setTodir(toDir); + FileSet fileset = new FileSet(); + fileset.setDir(fromDir); + if (null != includes) { + fileset.setIncludes(includes); + } + if (null != excludes) { + fileset.setExcludes(excludes); + } + copy.addFileset(fileset); + executeTask(copy); + + return false; + } + + /** task.execute() and any advice */ + protected boolean executeTask(Task task) { + if (!buildingEnabled) { + return false; + } + task.execute(); + return true; + } + +} + + +// finally caught by failing to comply with proper ant initialization +// /** +// * Build a module that has a build script. +// * @param buildSpec the module to build +// * @param buildScript the script file +// * @throws BuildException if build fails +// */ +// private void buildByScript(BuildSpec buildSpec, File buildScript) +// throws BuildException { +// Ant ant = new Ant(); +// ant.setProject(getProject()); +// ant.setAntfile(buildScript.getAbsolutePath()); +// ant.setDescription("building module " + buildSpec.module); +// ant.setDir(buildScript.getParentFile()); +// ant.setInheritAll(true); +// ant.setInheritRefs(false); +// ant.setLocation(getLocation()); +// ant.setOwningTarget(getOwningTarget()); +// // by convention, for build.xml, use module name to publish +// ant.setTarget(buildSpec.module); +// ant.setTaskName("ant"); +// loadAntProperties(ant, buildSpec); +// ant.execute(); +// } +// +// /** override definitions */ +// private void loadAntProperties(Ant ant, BuildSpec buildSpec) { +// Property property = ant.createProperty(); +// property.setName(BuildSpec.baseDir_NAME); +// property.setFile(buildSpec.baseDir); +// property = ant.createProperty(); +// property.setName(buildSpec.distDir_NAME); +// property.setFile(buildSpec.distDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.tempDir_NAME); +// property.setFile(buildSpec.tempDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.jarDir_NAME); +// property.setFile(buildSpec.jarDir); +// property = ant.createProperty(); +// property.setName(BuildSpec.stagingDir_NAME); +// property.setFile(buildSpec.stagingDir); +// } + + +/** + * Segregate product-building API's from module-building APIs for clarity. + * These are called by the superclass if the BuildSpec warrants. + * XXX extremely brittle/arbitrary assumptions. + * @see BuildModule for assumptions + */ +class ProductBuilder extends AntBuilder { + + private static String getProductInstallResourcesSrc(BuildSpec buildSpec) { + final String resourcesName = "installer-resources"; // XXXFileLiteral + File dir = buildSpec.productDir.getParentFile(); + String result = null; + if (null == dir) { + return "../../" + resourcesName; + } + dir = dir.getParentFile(); + if (null == dir) { + return "../" + resourcesName; + } else { + dir = new File(dir, resourcesName); + return dir.getPath(); + } + } + + private static String getProductInstallerFileName(BuildSpec buildSpec) { // XXXFileLiteral + return "aspectj-" + + buildSpec.productDir.getName() + + "-" + + Util.shortVersion(buildSpec.version) + + ".jar"; + } + + /** + * Calculate name of main, typically InitialCap, and hence installer class. + * @return $$installer$$.org.aspectj." + ProductName + "Installer" + */ + + private static String getProductInstallerMainClass(BuildSpec buildSpec) { + String productName = buildSpec.productDir.getName(); + String initial = productName.substring(0, 1).toUpperCase(); + productName = initial + productName.substring(1); + return "$installer$.org.aspectj." + productName + "Installer"; // XXXNameLiteral + } + + /** @see Builder.getBuilder(String, Project, File) */ + ProductBuilder( + Project project, + File tempDir, + boolean useEclipseCompiles, + Messager handler) { + super(project, tempDir, useEclipseCompiles, handler); + } + + /** + * Build product by discovering any modules to build, + * building those, assembling the product distribution, + * and optionally creating an installer for it. + * @return true on success + */ + protected boolean buildProduct(BuildSpec buildSpec) + throws BuildException { + Util.iaxIfNull(buildSpec, "buildSpec"); + // XXX if installer and not out of date, do not rebuild unless rebuild set + + if (!buildSpec.trimTesting) { + buildSpec.trimTesting = true; + handler.log("testing trimmed for " + buildSpec); + } + Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); + Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); + Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + + // ---- discover modules to build, and build them + Modules modules = new Modules( + buildSpec.baseDir, + buildSpec.jarDir, + buildSpec.trimTesting, + handler); + ProductModule[] productModules = discoverModules(buildSpec.productDir, modules); + for (int i = 0; i < productModules.length; i++) { + if (buildSpec.verbose) { + handler.log("building product module " + productModules[i]); + } + if (!buildProductModule(productModules[i])) { + return false; + } + } + if (buildSpec.verbose) { + handler.log("assembling product module for " + buildSpec); + } + + // ---- assemble product distribution + final String productName = buildSpec.productDir.getName(); + final File targDir = new File(buildSpec.distDir, productName); + final String targDirPath = targDir.getPath(); + if (targDir.canWrite()) { + Util.deleteContents(targDir); + } + + if (!targDir.canWrite() && !targDir.mkdirs()) { + if (buildSpec.verbose) { + handler.log("unable to create " + targDir); + } + return false; + } + // filter-copy everything but the binaries + Copy copy = makeCopyTask(true); + copy.setTodir(targDir); + File distDir = new File(buildSpec.productDir, "dist"); // XXXFileLiteral + Util.iaxIfNotCanReadDir(distDir, "product dist directory"); + FileSet fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setExcludes(Builder.BINARY_SOURCE_PATTERN); + copy.addFileset(fileset); + if (!executeTask(copy)) { + return false; + } + + // copy binaries (but not module flag files) + String excludes = null; + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < productModules.length; i++) { + if (0 < buf.length()) { + buf.append(","); + } + buf.append(productModules[i].relativePath); + } + if (0 < buf.length()) { + excludes = buf.toString(); + } + } + copy = makeCopyTask(false); + copy.setTodir(targDir); + fileset = new FileSet(); + fileset.setDir(distDir); + fileset.setIncludes(Builder.BINARY_SOURCE_PATTERN); + if (null != excludes) { + fileset.setExcludes(excludes); + } + copy.addFileset(fileset); + if (!executeTask(copy)) { + return false; + } + + // copy binaries associated with module flag files + for (int i = 0; i < productModules.length; i++) { + ProductModule product = productModules[i]; + String targPath = targDirPath + "/" + product.relativePath; + File jarFile = (product.assembleAll + ? product.module.getAssembledJar() + : product.module.getModuleJar() ); + copyFile(jarFile, new File(targPath), FILTER_OFF); + } + handler.log("created product in " + targDir); + // ---- create installer + if (buildSpec.createInstaller) { + return buildInstaller(buildSpec, targDirPath); + } else { + return true; + } + } + + protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath) { + if (buildSpec.verbose) { + handler.log("creating installer for " + buildSpec); + } + AJInstaller installer = new AJInstaller(); + installer.setProject(project); + installer.setBasedir(targDirPath); + //installer.setCompress(); + File installSrcDir = new File(buildSpec.productDir, "install"); // XXXFileLiteral + Util.iaxIfNotCanReadDir(installSrcDir, "installSrcDir"); + installer.setHtmlSrc(installSrcDir.getPath()); + String resourcePath = getProductInstallResourcesSrc(buildSpec); + File resourceSrcDir = new File(resourcePath); + Util.iaxIfNotCanReadDir(resourceSrcDir, "resourceSrcDir"); + installer.setResourcesSrc(resourcePath); + String name = getProductInstallerFileName(buildSpec); + File outFile = new File(buildSpec.jarDir, name); + installer.setZipfile(outFile.getPath()); + installer.setMainclass(getProductInstallerMainClass(buildSpec)); + installer.setInstallerclassjar(getBuildJar(buildSpec)); + return executeTask(installer); + + // -- test installer XXX + // create text setup file + // run installer with setup file + // cleanup installed product + } + + private String getBuildJar(BuildSpec buildSpec) { + return buildSpec.baseDir.getPath() + + "/lib/build/build.jar" ; // XXX + } + + private Module moduleForReplaceFile(File replaceFile, Modules modules) { + String jarName = moduleAliasFor(replaceFile.getName().toLowerCase()); + if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + jarName = jarName.substring(0, jarName.length()-4); + } else { + throw new IllegalArgumentException("can only replace .[jar|zip]"); + } + boolean assembleAll = jarName.endsWith("-all"); + String name = (!assembleAll ? jarName : jarName.substring(0, jarName.length()-4)); + return modules.getModule(name); + } + +} + + +class ProjectMessager extends Messager { + private final Project project; + public ProjectMessager(Project project) { + Util.iaxIfNull(project, "project"); + this.project = project; + } + + public boolean log(String s) { + project.log(s); + return true; + } + public boolean error(String s) { + project.log(s, project.MSG_ERR); + return true; + } + public boolean logException(String context, Throwable thrown) { + project.log(context + Util.renderException(thrown), project.MSG_ERR); + return true; + } + +}
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java new file mode 100644 index 000000000..a0f56f1d6 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/BuildModule.java @@ -0,0 +1,156 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Location; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.Path; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Builder; + +import java.io.File; + +/** + * Ant interface to build a product or module, including any required modules. + * @see Builder + */ +public class BuildModule extends Task { // quickie hack... + + public static void main(String[] args) { + TestBuildModule.main(args); + } + + private static File pathToFile(Path path) { + if (null != path) { + String[] list = path.list(); + if ((null == list) || (1 != list.length)) { + throw new IllegalArgumentException("expected exactly 1 element"); + } + return new File(list[0]); + } + return null; + } + BuildSpec buildSpec; + + public BuildModule() { + buildSpec = new BuildSpec(); + buildSpec.version = BuildSpec.BUILD_VERSION_DEFAULT; + } + + public void setModuledir(Path moduleDir) { + buildSpec.moduleDir = pathToFile(moduleDir); + } + + public void setModule(String module) { // XXX handle multiple modules, same builder + buildSpec.module = module; + } + + public void setVersion(String version) { + buildSpec.version = version; + } + public void setBasedir(Path baseDir) { + buildSpec.baseDir = pathToFile(baseDir); + } + + public void setJardir(Path jarDir) { + buildSpec.jarDir = pathToFile(jarDir); + } + + public void setTrimtesting(boolean trimTesting) { + buildSpec.trimTesting = trimTesting; + } + + public void setAssembleall(boolean assembleAll) { + buildSpec.assembleAll = assembleAll; + } + + public void setRebuild(boolean rebuild) { + buildSpec.rebuild = rebuild; + } + + public void setFailonerror(boolean failonerror) { + buildSpec.failonerror = failonerror; + } + + public void setCreateinstaller(boolean create) { + buildSpec.createInstaller = create; + } + + public void setVerbose(boolean verbose) { + buildSpec.verbose = verbose; + } + + public void setBuildConfig(String buildConfig) { + buildSpec.buildConfig = buildConfig; + } + + // --------------------------------------------------------- product build + + public void setProductdir(Path productDir) { + buildSpec.productDir = pathToFile(productDir); + } + + public void setTempdir(Path tempDir) { + buildSpec.tempDir = pathToFile(tempDir); + } + + public void setDistdir(Path distdir) { + buildSpec.distDir = pathToFile(distdir); + } + + public void execute() throws BuildException { + final BuildSpec buildSpec = this.buildSpec; + this.buildSpec = new BuildSpec(); + build(buildSpec); + } + + private void build(BuildSpec buildSpec) throws BuildException { + final boolean failonerror = buildSpec.failonerror; + Builder builder = null; + try { + File buildScript = new File(buildSpec.moduleDir, "build.xml"); // XXXFileLiteral + if (buildScript.canRead()) { + if (!buildByScript(buildSpec, buildScript)) { + log("unable to build " + buildSpec + " using script: " + buildScript); + } + } else { + builder = AntBuilder.getBuilder( + buildSpec.buildConfig, + getProject(), + buildSpec.tempDir); + if (!builder.build(buildSpec) && failonerror) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, loc); + } + } + } catch (BuildException e) { + throw e; + } catch (Throwable t) { + Location loc = getLocation(); + throw new BuildException("error building " + buildSpec, t, loc); + } finally { + if (null != builder) { + builder.cleanup(); + } + } + } + + boolean buildByScript(BuildSpec buildSpec, File buildScript) + throws BuildException { + return false; + } +} +
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java new file mode 100644 index 000000000..d79c9774f --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/Checklics.java @@ -0,0 +1,735 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; +import java.util.*; + +/** + * Check that included .java files contain license and copyright strings + * for MPL 1.0 (default), Apache, or CPL. + * Use list="true" to get a list of known license variants {license}-{copyrightHolder} + * todo reimplement with regexp and jdiff FileLine utilities + */ +public class Checklics extends MatchingTask { + /* + This does not enforce that copyrights are correct/current, + only that they exist. + E.g., the default behavior requires MPL but permits either + Xerox or PARC copyright holders and any valid year. + */ + public static final String MPL_TAG = "mpl"; + public static final String APACHE_TAG = "apache"; + public static final String CPL_IBM_PARC_TAG = "cpl-ibm|parc"; + public static final String CPL_IBM_TAG = "cpl-ibm"; + public static final String MPL_XEROX_PARC_TAG = "mpl-parc|xerox"; + public static final String MPL_ONLY_TAG = "mpl-only"; + public static final String MPL_PARC_TAG = "mpl-parc"; + public static final String PARC_COPYRIGHT_TAG = "parc-copy"; + public static final String CPL_IBM_PARC_XEROX_TAG = "cpl-ibm|parc|xerox"; + public static final String DEFAULT = CPL_IBM_PARC_XEROX_TAG; + + static final Hashtable LICENSES; // XXX unmodifiable Map + + static { + final String XEROX = "Xerox"; + final String PARC = "Palo Alto Research Center"; + final String APACHE = "The Apache Software Foundation"; + final String IBM = "IBM"; + final String IBM_LONG = "International Business Machines"; + final String LIC_APL = + "Apache Software Foundation (http://www.apache.org/)"; + // XXX + final String LIC_MPL = "http://aspectj.org/MPL/"; + //final String LIC_CPL = "http://www.eclipse.org/legal/cpl-v"; // XXX not versioned + final String LIC_CPL = "Common Public License"; + // XXX not versioned + License APL = new License(APACHE_TAG, LIC_APL, APACHE); + License MPL = new License(MPL_TAG, LIC_MPL, XEROX); + License MPL_XEROX_PARC = new License(DEFAULT, LIC_MPL, XEROX, PARC); + License CPL_IBM_PARC = new License(CPL_IBM_PARC_TAG,LIC_CPL, + new String[] { IBM_LONG, IBM, PARC }); + License CPL_IBM_PARC_XEROX = new License(CPL_IBM_PARC_XEROX_TAG,LIC_CPL, + new String[] { IBM_LONG, IBM, PARC, XEROX }); + License CPL_IBM = new License(CPL_IBM_TAG, LIC_CPL, IBM, IBM_LONG); + License MPL_ONLY = new License(MPL_ONLY_TAG, LIC_MPL); + License MPL_PARC = new License(MPL_PARC_TAG, LIC_MPL, PARC); + License PARC_COPYRIGHT = new License(PARC_COPYRIGHT_TAG, null, PARC); + LICENSES = new Hashtable(); + LICENSES.put(APL.tag, APL); + LICENSES.put(MPL.tag, MPL); + LICENSES.put(MPL_PARC.tag, MPL_PARC); + LICENSES.put(MPL_XEROX_PARC.tag, MPL_XEROX_PARC); + LICENSES.put(CPL_IBM_PARC.tag, CPL_IBM_PARC); + LICENSES.put(MPL_ONLY.tag, MPL_ONLY); + LICENSES.put(CPL_IBM.tag, CPL_IBM); + LICENSES.put(PARC_COPYRIGHT.tag, PARC_COPYRIGHT); + LICENSES.put(CPL_IBM_PARC_XEROX.tag, CPL_IBM_PARC_XEROX); + } + + /** @param args String[] { < sourcepath > {, < licenseTag > } } */ + public static void main(String[] args) { + switch (args.length) { + case 1 : runDirect(args[0], null); + break; + case 2 : runDirect(args[0], args[1]); + break; + default: + String options = "{replace-headers|get-years|list|{licenseTag}}"; + System.err.println("java {me} sourcepath " + options); + break; + } + } + + /** + * Run the license check directly + * @param sourcepaths String[] of paths to source directories + * @param license the String tag for the license, if any + * @throws IllegalArgumentException if sourcepaths is empty + * @return total number of failed licenses + */ + public static int runDirect(String sourcepath, String license) { + if ((null == sourcepath) || (1 > sourcepath.length())) { + throw new IllegalArgumentException("bad sourcepath: " + sourcepath); + } + Checklics me = new Checklics(); + Project p = new Project(); + p.setName("direct interface to Checklics"); + p.setBasedir("."); + me.setProject(p); + me.setSourcepath(new Path(p, sourcepath)); + if (null != license) { + if ("replace-headers".equals(license)) { + me.setReplaceheaders(true); + } else if ("get-years".equals(license)) { + me.setGetYears(true); + } else if ("list".equals(license)) { + me.setList(true); + } else { + me.setLicense(license); + } + } + me.execute(); + return me.failed; + } + + private Path sourcepath; + private License license; + private boolean list; + private String streamTag; + private boolean failOnError; + private boolean getYears; + private boolean replaceHeaders; + private int passed; + private int failed; + + private boolean printDirectories; + + /** @param list if true, don't run but list known license tags */ + public void setList(boolean list) { + this.list = list; + } + + public void setPrintDirectories(boolean print) { + printDirectories = print; + } + + /** + * When failOnError is true, if any file failed, throw BuildException + * listing number of files that file failed to pass license check + * @param fail if true, report errors by throwing BuildException + */ + public void setFailOnError(boolean fail) { + this.failOnError = fail; + } + + /** @param tl mpl | apache | cpl */ + public void setLicense(String tl) { + License input = (License) LICENSES.get(tl); + if (null == input) { + throw new BuildException("no license known for " + tl); + } + license = input; + } + + public void setSourcepath(Path path) { + if (sourcepath == null) { + sourcepath = path; + } else { + sourcepath.append(path); + } + } + + public Path createSourcepath() { + return sourcepath == null + ? (sourcepath = new Path(project)) + : sourcepath.createPath(); + } + + public void setSourcepathRef(Reference id) { + createSourcepath().setRefid(id); + } + + /** @param out "out" or "err" */ + public void setOutputStream(String out) { + this.streamTag = out; + } + + public void setReplaceheaders(boolean replaceHeaders) { + this.replaceHeaders = replaceHeaders; + } + + public void setGetYears(boolean getYears) { + this.getYears = getYears; + } + + /** list known licenses or check source tree */ + public void execute() throws BuildException { + if (list) { + list(); + } else if (replaceHeaders) { + replaceHeaders(); + } else if (getYears) { + getYears(); + } else { + checkLicenses(); + } + } + + private PrintStream getOut() { + return ("err".equals(streamTag) ? System.err : System.out); + } + + interface FileVisitor { + void visit(File file); + } + + /** visit all .java files in all directories... */ + private void visitAll(FileVisitor visitor) { + List filelist = new ArrayList(); + String[] dirs = sourcepath.list(); + for (int i = 0; i < dirs.length; i++) { + File dir = project.resolveFile(dirs[i]); + String[] files = getDirectoryScanner(dir).getIncludedFiles(); + for (int j = 0; j < files.length; j++) { + File file = new File(dir, files[j]); + String path = file.getPath(); + if (path.endsWith(".java")) { + visitor.visit(file); + } + } + } + } + + private void replaceHeaders() { + class YearVisitor implements FileVisitor { + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + if (!Header.replaceHeader(file, info)) { + throw new BuildException("failed to replace header for " + file + + " using " + info); + } + } + } + visitAll(new YearVisitor()); + } + + private void getYears() { + final PrintStream out = getOut(); + class YearVisitor implements FileVisitor { + public void visit(File file) { + HeaderInfo info = Header.checkFile(file); + out.println(info.toString()); + } + } + visitAll(new YearVisitor()); + } + + private void checkLicenses() throws BuildException { + if (null == license) { + setLicense(DEFAULT); + } + final License license = this.license; // being paranoid... + if (null == license) { + throw new BuildException("no license"); + } + final PrintStream out = getOut(); + + class Visitor implements FileVisitor { + int failed = 0; + int passed = 0; + public void visit(File file) { + if (license.checkFile(file)) { + passed++; + } else { + failed++; + String path = file.getPath(); + if (!license.foundLicense()) { + out.println( + license.tag + " LICENSE FAIL: " + path); + } + if (!license.foundCopyright()) { + out.println( + license.tag + " COPYRIGHT FAIL: " + path); + } + } + } + } + Visitor visitor = new Visitor(); + visitAll(visitor); + getOut().println( + "Total passed: " + + visitor.passed + + (visitor.failed == 0 ? "" : " failed: " + visitor.failed)); + if (failOnError && (0 < visitor.failed)) { + throw new BuildException( + failed + " files failed license check"); + } + } + + private void oldrun() throws BuildException { + if (list) { + list(); + return; + } + if (null == license) { + setLicense(DEFAULT); + } + final License license = this.license; // being paranoid... + if (null == license) { + throw new BuildException("no license"); + } + final PrintStream out = getOut(); + + List filelist = new ArrayList(); + String[] dirs = sourcepath.list(); + failed = 0; + passed = 0; + for (int i = 0; i < dirs.length; i++) { + int dirFailed = 0; + int dirPassed = 0; + File dir = project.resolveFile(dirs[i]); + String[] files = getDirectoryScanner(dir).getIncludedFiles(); + for (int j = 0; j < files.length; j++) { + File file = new File(dir, files[j]); + String path = file.getPath(); + if (path.endsWith(".java")) { + if (license.checkFile(file)) { + dirPassed++; + } else { + dirFailed++; + if (!license.foundLicense()) { + out.println( + license.tag + " LICENSE FAIL: " + path); + } + if (!license.foundCopyright()) { + out.println( + license.tag + " COPYRIGHT FAIL: " + path); + } + } + } + } + if (printDirectories) { + out.println( + "dir: " + + dirs[i] + + " passed: " + + dirPassed + + (dirFailed == 0 ? "" : " failed: " + dirFailed)); + } + failed += dirFailed; + passed += dirPassed; + } + } + + private void list() { + Enumeration enum = LICENSES.keys(); + StringBuffer sb = new StringBuffer(); + sb.append("known license keys:"); + boolean first = true; + while (enum.hasMoreElements()) { + sb.append((first ? " " : ", ") + enum.nextElement()); + if (first) { + first = false; + } + } + getOut().println(sb.toString()); + } + + + /** + * Encapsulate license and copyright specifications + * to check files use hokey string matching. + */ + public static class License { + /** acceptable years for copyright prefix to company - append " " */ + static final String[] YEARS = // XXX remove older after license xfer? + new String[] { "2002 ", "2003 ", "2001 ", "2000 ", "1999 " }; + public final String tag; + public final String license; + private final String[] copyright; + private boolean gotLicense; + private boolean gotCopyright; + + License(String tag, String license) { + this(tag, license, (String[]) null); + } + + License(String tag, String license, String copyright) { + this(tag, license, new String[] { copyright }); + } + + License( + String tag, + String license, + String copyright, + String altCopyright) { + this(tag, license, new String[] { copyright, altCopyright }); + } + + License(String tag, String license, String[] copyright) { + this.tag = tag; + if ((null == tag) || (0 == tag.length())) { + throw new IllegalArgumentException("null tag"); + } + this.license = license; + this.copyright = copyright; + } + + public final boolean gotValidFile() { + return foundLicense() && foundCopyright(); + } + + /** @return true if no license sought or if some license found */ + public final boolean foundLicense() { + return ((null == license) || gotLicense); + } + + /** @return true if no copyright sought or if some copyright found */ + public final boolean foundCopyright() { + return ((null == copyright) || gotCopyright); + } + + public boolean checkFile(final File file) { + clear(); + boolean result = false; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (!gotValidFile() + && (line = input.readLine()) != null) { + lineNum++; + checkLine(line); + } + } catch (IOException e) { + System.err.println( + "reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return gotValidFile(); + } + + public String toString() { + return tag; + } + + private void checkLine(String line) { + if ((null == line) || (0 == line.length())) { + return; + } + if (!gotLicense + && (null != license) + && (-1 != line.indexOf(license))) { + gotLicense = true; + } + if (!gotCopyright && (null != copyright)) { + int loc; + for (int j = 0; + !gotCopyright && (j < YEARS.length); + j++) { + if (-1 != (loc = line.indexOf(YEARS[j]))) { + loc += YEARS[j].length(); + String afterLoc = line.substring(loc).trim(); + for (int i = 0; + !gotCopyright && (i < copyright.length); + i++) { + if (0 == afterLoc.indexOf(copyright[i])) { + gotCopyright = true; + } + } + } + } + } + } + + private void clear() { + if (gotLicense) { + gotLicense = false; + } + if (gotCopyright) { + gotCopyright = false; + } + } + } // class License +} + +class HeaderInfo { + /** File for which this is the info */ + public final File file; + + /** unmodifiable List of String years */ + public final List years; + + /** last line of license */ + public final int lastLine; + + /** last line of license */ + public final boolean hasLicense; + + public HeaderInfo(File file, int lastLine, List years, boolean hasLicense) { + this.lastLine = lastLine; + this.file = file; + this.hasLicense = hasLicense; + List newYears = new ArrayList(); + newYears.addAll(years); + Collections.sort(newYears); + this.years = Collections.unmodifiableList(newYears); + if ((null == file) || !file.canWrite()) { + throw new IllegalArgumentException("bad file: " + this); + } + if (!hasLicense) { + if ((0> lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } else { + if ((null == years) || (1 > years.size())) { + throw new IllegalArgumentException("no years: " + this); + } + if ((20 > lastLine) || (65 < lastLine)) { + throw new IllegalArgumentException("bad last line: " + this); + } + } + } + + public String toString() { + return file.getPath() + ":" + lastLine + " " + years; + } + + public void writeHeader(PrintWriter writer) { + if (!hasLicense) { + writer.println(TOP); + writer.println(PARC_ONLY); + writeRest(writer); + } else { + final int size = years.size(); + if (1 > size) { + throw new Error("no years found in " + toString()); + } + String first = (String) years.get(0); + String last = (String) years.get(size-1); + boolean lastIs2002 = "2002".equals(last); + String xlast = last; + if (lastIs2002) { // 2002 was PARC + xlast = (String) (size > 1 ? years.get(size-2) : null); + // 1999-2002 Xerox implies 1999-2001 Xerox + if (first.equals(xlast) && !"2001".equals(xlast)) { + xlast = "2001"; + } + } + String xyears = first + "-" + xlast; + if (first.equals(last)) { + xyears = first; + } + + writer.println(TOP); + if (!lastIs2002) { // Xerox only + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ". "); + } else if (size == 1) { // PARC only + writer.println(PARC_ONLY); + } else { // XEROX plus PARC + writer.println(XEROX_PREFIX + xyears + XEROX_SUFFIX + ", "); + writer.println(PARC); + } + writeRest(writer); + } + } + + void writeRest(PrintWriter writer) { + writer.println(" * All rights reserved. "); + writer.println(" * This program and the accompanying materials are made available "); + writer.println(" * under the terms of the Common Public License v1.0 "); + writer.println(" * which accompanies this distribution and is available at "); + writer.println(" * http://www.eclipse.org/legal/cpl-v10.html "); + writer.println(" * "); + writer.println(" * Contributors: "); + writer.println(" * Xerox/PARC initial implementation "); + writer.println(" * ******************************************************************/"); + writer.println(""); + } + + public static final String TOP + = "/* *******************************************************************"; + public static final String PARC + = " * 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String PARC_ONLY + = " * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC)."; + public static final String XEROX_PREFIX + = " * Copyright (c) "; + public static final String XEROX_SUFFIX + = " Xerox Corporation"; + /* +/* ******************************************************************* + * Copyright (c) 1998-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ****************************************************************** + + */ +} + +/** + * header search/replace using hokey string matching + */ +class Header { + + /** replace the header in file */ + public static boolean replaceHeader(File file, HeaderInfo info) { + ArrayList years = new ArrayList(); + int endLine = 0; + BufferedReader input = null; + PrintWriter output = null; + FileWriter outWriter = null; + int lineNum = 0; + boolean result = false; + final File inFile = new File(file.getPath() + ".tmp"); + try { + File outFile = new File(file.getPath()); + if (!file.renameTo(inFile) || !inFile.canRead()) { + throw new Error("unable to rename " + file + " to " + inFile); + } + outWriter = new FileWriter(outFile); + input = new BufferedReader(new FileReader(inFile)); + output = new PrintWriter(outWriter, true); + info.writeHeader(output); + String line; + while (null != (line = input.readLine())) { + lineNum++; + if (lineNum > info.lastLine) { + output.println(line); + } + } + } catch (IOException e) { + System.err.println( + "writing line " + lineNum + " of " + file); + e.printStackTrace(System.err); + result = false; + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + result = false; + } + } + if (null != outWriter) { + try { + outWriter.close(); + } catch (IOException e) { + result = false; + } + } + result = inFile.delete(); + } + return result; + } + + public static HeaderInfo checkFile(final File file) { + ArrayList years = new ArrayList(); + int endLine = 0; + BufferedReader input = null; + int lineNum = 0; + try { + input = new BufferedReader(new FileReader(file)); + String line; + while (null != (line = input.readLine())) { + lineNum++; + String ll = line.trim(); + if (ll.startsWith("package ") + || ll.startsWith("import ")) { + break; // ignore default package w/o imports + } + if (checkLine(line, years)) { + endLine = lineNum; + break; + } + } + } catch (IOException e) { + System.err.println( + "reading line " + lineNum + " of " + file); + e.printStackTrace(System.err); + } finally { + if (null != input) { + try { + input.close(); + } catch (IOException e) { + } // ignore + } + } + return new HeaderInfo(file, endLine, years, endLine > 0); + } + + /** + * Add any years found (as String) to years, + * and return true at the first end-of-comment + * @return true if this line has end-of-comment + */ + private static boolean checkLine(String line, ArrayList years) { + if ((null == line) || (0 == line.length())) { + return false; + } + int loc; + int start = 0; + + while ((-1 != (loc = line.indexOf("199", start)) + || (-1 != (loc = line.indexOf("200", start))))) { + char c = line.charAt(loc + 3); + if ((c <= '9') && (c >= '0')) { + years.add(line.substring(loc, loc+4)); + } + start = loc + 4; + } + + return (-1 != line.indexOf("*/")); + } + +} // class Header + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java new file mode 100644 index 000000000..8b5184121 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/ConditionalTask.java @@ -0,0 +1,182 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import java.util.*; + +public abstract class ConditionalTask extends Task { + + public final static String TRUE = "true"; + + private List ifs; + protected List ifs() { + return ifs != null ? ifs : (ifs = new Vector()); + } + + public If createIf() { + If i = new If(); + ifs().add(i); + return i; + } + + public If createIf(String name, String equals, boolean strict) { + If i = createIf(); + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + return i; + } + + public If createIf(String name, String equals) { + return createIf(name, equals, false); + } + + public If createIf(String name) { + return createIf(name, TRUE, false); + } + + public If createIf(String name, boolean strict) { + return createIf(name, TRUE, strict); + } + + public void setIfs(String ifs) { + StringTokenizer tok = new StringTokenizer(ifs, ",;: ", false); + while (tok.hasMoreTokens()) { + String next = tok.nextToken(); + int iequals = next.lastIndexOf("="); + String equals; + String name; + boolean strict; + If i = createIf(); + if (iequals != -1) { + name = next.substring(0, iequals); + equals = next.substring(iequals + 1); + strict = true; + } else { + name = next.substring(0); + equals = TRUE; + strict = false; + } + i.setName(name); + i.setEquals(equals); + i.setStrict(strict); + } + } + + public void setIf(String ifStr) { + setIfs(ifStr); + } + + public class If { + public If() { + this(null, null); + } + public If(String name) { + this(name, TRUE); + } + public If(String name, String equals) { + setName(name); + setEquals(equals); + } + private String name; + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + private String equals; + public void setEquals(String equals) { + this.equals = equals; + } + public String getEquals() { + return equals; + } + private boolean strict = false; + public void setStrict(boolean strict) { + this.strict = strict; + } + public boolean isStrict() { + return strict; + } + public boolean isOk(String prop) { + return isOk(prop, isStrict()); + } + //XXX Need a better boolean parser + public boolean isOk(String prop, boolean isStrict) { + if (isStrict) { + return prop != null && prop.equals(getEquals()); + } else { + if (isOk(prop, true)) { + return true; + } + if (prop == null || isFalse(getEquals())) { + return true; + } + if ( (isTrue(getEquals()) && isTrue(prop)) || + (isFalse(getEquals()) && isFalse(prop)) ) { + return true; + } + return false; + } + } + private boolean isFalse(String prop) { + return isOneOf(prop, falses) || isOneOf(prop, complement(trues)); + } + private boolean isTrue(String prop) { + return isOneOf(prop, trues) || isOneOf(prop, complement(falses)); + } + private boolean isOneOf(String prop, String[] strings) { + for (int i = 0; i < strings.length; i++) { + if (strings[i].equals(prop)) { + return true; + } + } + return false; + } + private String[] complement(String[] strings) { + for (int i = 0; i < strings.length; i++) { + strings[i] = "!" + strings[i]; + } + return strings; + } + } + + final static String[] falses = { "false", "no" }; + final static String[] trues = { "true", "yes" }; + + protected boolean checkIfs() { + return getFalses().size() == 0; + } + + protected List getFalses() { + Iterator iter = ifs().iterator(); + List result = new Vector(); + while (iter.hasNext()) { + If next = (If) iter.next(); + String name = next.getName(); + String prop = project.getProperty(name); + if (prop == null) { + prop = project.getUserProperty(name); + } + if (!next.isOk(prop)) { + result.add(name); + } + } + return result; + } + + public abstract void execute() throws BuildException; +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java new file mode 100644 index 000000000..bafe5da2b --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/CopyAndInlineStylesheet.java @@ -0,0 +1,113 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; + +public class CopyAndInlineStylesheet extends Task { + + private File file; + public void setFile(String file) { + this.file = project.resolveFile(file); + } + + private File todir; + public void setTodir(String todir) { + this.todir = project.resolveFile(todir); + } + + + public void execute() throws BuildException { + try { + if (todir == null) { + throw new BuildException("must set 'todir' attribute"); + } + if (file == null) { + throw new BuildException("must set 'file' attribute"); + } + log("copying html from" + file + " to " + todir.getAbsolutePath()); + + File toFile = new File(todir, file.getName()); + + Mkdir mkdir = (Mkdir) project.createTask("mkdir"); + mkdir.setDir(todir); + mkdir.execute(); + + BufferedReader in = new BufferedReader(new FileReader(file)); + PrintStream out = new PrintStream(new FileOutputStream(toFile)); + + outer: + while (true) { + String line = in.readLine(); + if (line == null) break; + if (isStyleSheet(line)) { + doStyleSheet(line, out, file); + while (true) { + String line2 = in.readLine(); + if (line2 == null) break outer; + out.println(line2); + } + } else { + out.println(line); + } + } + + in.close(); + out.close(); + } catch (IOException e) { + throw new BuildException(e.getMessage()); + } + } + + private static void doStyleSheet(String line, PrintStream out, File file) throws IOException { + int srcIndex = line.indexOf("href"); + int startQuotIndex = line.indexOf('"', srcIndex); + int endQuotIndex = line.indexOf('"', startQuotIndex + 1); + + String stylesheetLocation = line.substring(startQuotIndex + 1, endQuotIndex); + + File styleSheetFile = new File(file.getParent(), stylesheetLocation); + + out.println("<style type=\"text/css\">"); + out.println("<!--"); + + BufferedReader inStyle = new BufferedReader(new FileReader(styleSheetFile)); + + while (true) { + String line2 = inStyle.readLine(); + if (line2 == null) break; + out.println(line2); + } + inStyle.close(); + + out.println("-->"); + out.println("</style>"); + } + + + private static boolean isStyleSheet(String line) throws IOException { + line = line.toLowerCase(); + int len = line.length(); + int i = 0; + + while (true) { + if (i == len) return false; + if (! Character.isWhitespace(line.charAt(i))) break; + } + + return line.startsWith("<link", i); + } +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java new file mode 100644 index 000000000..0a019707c --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/StripNonBodyHtml.java @@ -0,0 +1,233 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.*; +import org.apache.tools.ant.taskdefs.*; +import java.io.*; + +/** + * Task to convert html source files into files with only body content. + * + * <p> This task can take the following arguments:</p> + * + * <ul> + * <li>srcdir</li> + * <li>destdir</li> + * <li>include</li> + * <li>exclude</li> + * </ul> + * + * <p>Of these arguments, only <b>sourcedir</b> is required.</p> + * + * <p> When this task executes, it will scan the srcdir based on the + * include and exclude properties.</p> + */ + +public class StripNonBodyHtml extends MatchingTask { + + private File srcDir; + private File destDir = null; + + public void setSrcdir(File srcDir) { + this.srcDir = srcDir; + } + + public void setDestdir(File destDir) { + this.destDir = destDir; + } + + public void execute() throws BuildException { + if (srcDir == null) { + throw new BuildException("srcdir attribute must be set!"); + } + if (!srcDir.exists()) { + throw new BuildException("srcdir does not exist!"); + } + if (!srcDir.isDirectory()) { + throw new BuildException("srcdir is not a directory!"); + } + if (destDir != null) { + if (!destDir.exists()) { + throw new BuildException("destdir does not exist!"); + } + if (!destDir.isDirectory()) { + throw new BuildException("destdir is not a directory!"); + } + } + + DirectoryScanner ds = super.getDirectoryScanner(srcDir); + String[] files = ds.getIncludedFiles(); + + log("stripping " + files.length + " files"); + int stripped = 0; + for (int i = 0, len = files.length; i < len; i++) { + if (processFile(files[i])) { + stripped++; + } else { + log(files[i] + " not stripped"); + } + } + log(stripped + " files successfully stripped"); + } + + boolean processFile(String filename) throws BuildException { + File srcFile = new File(srcDir, filename); + File destFile; + if (destDir == null) { + destFile = srcFile; + } else { + destFile = new File(destDir, filename); + destFile.getParentFile().mkdirs(); + } + try { + return strip(srcFile, destFile); + } catch (IOException e) { + throw new BuildException(e); + } + } + + private boolean strip(File f, File g) throws IOException { + BufferedInputStream in = + new BufferedInputStream(new FileInputStream(f)); + String s = readToString(in); + in.close(); + return writeBodyTo(s, g); + } + + private ByteArrayOutputStream temp = new ByteArrayOutputStream(); + private byte[] buf = new byte[2048]; + + private String readToString(InputStream in) throws IOException { + ByteArrayOutputStream temp = this.temp; + byte[] buf = this.buf; + String s = ""; + try { + while (true) { + int i = in.read(buf, 0, 2048); + if (i == -1) break; + temp.write(buf, 0, i); + + } + s = temp.toString(); + } finally { + temp.reset(); + } + return s; + } + + private boolean writeBodyTo(String s, File f) throws IOException { + int start, end; + try { + start = findStart(s); + end = findEnd(s, start); + } catch (ParseException e) { + return false; // if we get confused, just don't write the file. + } + s = processBody(s,f); + BufferedOutputStream out = + new BufferedOutputStream(new FileOutputStream(f)); + + out.write(s.getBytes()); + out.close(); + return true; + } + + /** + * Process body. This implemenation strips text + * between <!-- start strip --> + * and <!-- end strip --> + * inclusive. + */ + private String processBody(String body, File file) { + if (null == body) return body; + final String START = "<!-- start strip -->"; + final String END = "<!-- end strip -->"; + return stripTags(body, file.toString(), START, END); + } + + /** + * Strip 0..n substrings in input: "s/${START}.*${END}//g" + * @param input the String to strip + * @param source the name of the source for logging purposes + * @param start the starting tag (case sensitive) + * @param end the ending tag (case sensitive) + */ + String stripTags(String input, final String SOURCE, + final String START, final String END) { + if (null == input) return input; + StringBuffer buffer = new StringBuffer(input.length()); + String result = input; + int curLoc = 0; + while (true) { + int startLoc = input.indexOf(START, curLoc); + if (-1 == startLoc) { + buffer.append(input.substring(curLoc)); + result = buffer.toString(); + break; // <------------ valid exit + } else { + int endLoc = input.indexOf(END, startLoc); + if (-1 == endLoc) { + log(SOURCE + " stripTags - no end tag - startLoc=" + startLoc); + break; // <------------ invalid exit + } else if (endLoc < startLoc) { + log(SOURCE + " stripTags - impossible: startLoc=" + + startLoc + " > endLoc=" + endLoc); + break; // <------------ invalid exit + } else { + buffer.append(input.substring(curLoc, startLoc)); + curLoc = endLoc + END.length(); + } + } + } + return result; + } + + private int findStart(String s) throws ParseException { + int len = s.length(); + int start = 0; + while (true) { + start = s.indexOf("<body", start); + if (start == -1) { + start = s.indexOf("<BODY", start); + if (start == -1) throw barf(); + } + start = start + 5; + if (start >= len) throw barf(); + char ch = s.charAt(start); + if (ch == '>') return start + 1; + if (Character.isWhitespace(ch)) { + start = s.indexOf('>', start); + if (start == -1) return -1; + return start + 1; + } + } + } + + private int findEnd(String s, int start) throws ParseException { + int end; + end = s.indexOf("</body>", start); + if (end == -1) { + end = s.indexOf("</BODY>", start); + if (end == -1) throw barf(); + } + return end; + } + + private static class ParseException extends Exception {} + + private static ParseException barf() { + return new ParseException(); + } +} diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java b/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java new file mode 100644 index 000000000..374f71d79 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/TestBuildModule.java @@ -0,0 +1,79 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.ant.taskdefs; + +import org.apache.tools.ant.Project; +import org.aspectj.internal.tools.build.*; +import org.aspectj.internal.tools.build.BuildSpec; +import org.aspectj.internal.tools.build.Util; + +import java.io.File; +import java.util.Arrays; + +public class TestBuildModule { + private static boolean REBUILD = false; + private static final String SYNTAX = "java {classname} <[product|module]dir>"; + public static void main(String[] args) { + + if ((null == args) || (1 > args.length) + || !Util.canReadDir(new File(args[0]))) { + System.err.println(SYNTAX); + return; + } + File dir = new File(args[0]); + // create a module + if (Util.canReadDir(new File(dir, "dist"))) { + createProduct(args); + } else if (Util.canReadFile(new File(dir, ".classpath"))) { + createModule(args); + } else { + System.err.println(SYNTAX); + } + } + + static void createModule(String[] args) { + File moduleDir = new File(args[0]); + File baseDir = moduleDir.getParentFile(); + if (null == baseDir) { + baseDir = new File("."); + } + File jarDir = new File(baseDir, "aj-build-jars"); + if (!(Util.canReadDir(jarDir) || jarDir.mkdirs())) { + System.err.println("unable to create " + jarDir); + return; + } + + // set module dir or basedir plus module name + BuildSpec buildSpec = new BuildSpec(); + buildSpec.moduleDir = moduleDir; + buildSpec.jarDir = jarDir; + buildSpec.verbose = true; + buildSpec.failonerror = true; + buildSpec.trimTesting = true; + buildSpec.rebuild = true; + + File tempDir = null; + Project project = new Project(); + project.setProperty("verbose", "true"); + project.setName("TestBuildModule.createModule" + Arrays.asList(args)); + Builder builder = AntBuilder.getBuilder("", project, tempDir); + builder.build(buildSpec); + } + + static void createProduct(String[] args) { + throw new Error("unimplemented"); + } +} + diff --git a/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties b/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties new file mode 100644 index 000000000..37da66e9e --- /dev/null +++ b/build/src/org/aspectj/internal/tools/ant/taskdefs/taskdefs.properties @@ -0,0 +1,20 @@ +ajinstaller=org.aspectj.internal.tools.ant.taskdefs.AJInstaller +ajpush=org.aspectj.internal.tools.ant.taskdefs.AJPush +ajbuild=org.aspectj.internal.tools.ant.taskdefs.BuildModule +checklics=org.aspectj.internal.tools.ant.taskdefs.Checklics +stripnonbodyhtml=org.aspectj.internal.tools.ant.taskdefs.StripNonBodyHtml + +# ajclean=org.aspectj.internal.tools.ant.taskdefs.AJclean +# ajcvs=org.aspectj.internal.tools.ant.taskdefs.Ajcvs +# ajikes=org.aspectj.internal.tools.ant.taskdefs.AJikes +# ajinit=org.aspectj.internal.tools.ant.taskdefs.AjInit +# checkrelease=org.aspectj.internal.tools.ant.taskdefs.Checkrelease +# clear=org.aspectj.internal.tools.ant.taskdefs.Clear +# inlinestylesheetaj=org.aspectj.internal.tools.ant.taskdefs.CopyAndInlineStyleshee +# ensure=org.aspectj.internal.tools.ant.taskdefs.Ensure +# ensureproperties=org.aspectj.internal.tools.ant.taskdefs.EnsureProperties +# newdir=org.aspectj.internal.tools.ant.taskdefs.Newdir +# overwrite=org.aspectj.internal.tools.ant.taskdefs.Overwrite +# props2filters=org.aspectj.internal.tools.ant.taskdefs.Props2Filters +# tgz=org.aspectj.internal.tools.ant.taskdefs.Tgz +# vmcheck=org.aspectj.internal.tools.ant.taskdefs.VMCheck diff --git a/build/src/org/aspectj/internal/tools/build/BuildSpec.java b/build/src/org/aspectj/internal/tools/build/BuildSpec.java new file mode 100644 index 000000000..bf9ccdea8 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/BuildSpec.java @@ -0,0 +1,161 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Properties; + +/** + * Open struct for specifying builds for both modules and products. + * Separated from bulder to permit this to build many modules + * concurrently. + * Static state has much of the Ant build properties (move?) + */ +public class BuildSpec { + public static final String baseDir_NAME = "aspectj.modules.dir"; + public static final String stagingDir_NAME = "aj.staging.dir"; + public static final String jarDir_NAME = "aj.jar.dir"; + public static final String tempDir_NAME = "aj.temp.dir"; + public static final String distDir_NAME = "aj.dist.dir"; + + /** name of a system property for reading the build version */ + public static final String SYSTEM_BUILD_VERSION_KEY = "aspectj.build.version"; + + /** value of the build.version if build version not defined */ + public static final String BUILD_VERSION_DEFAULT = "DEVELOPMENT"; + + /** name of a filter property for the normal build version */ + public static final String BUILD_VERSION_NAME = "build.version"; + + /** name of a filter property for the company */ + public static final String COMPANY_NAME = "company.name"; + + /** default value of of a filter property for the company */ + public static final String COMPANY_NAME_DEFAULT = "aspectj.org"; + + /** name of a filter property for the base build version (no alpha, etc.) */ + public static final String BUILD_VERSION_BASE_NAME = "build.version.base"; + + /** copyright property name */ + public static final String COPYRIGHT_NAME = "copyright.allRights.from1998"; + + /** overall copyright */ + public static final String COPYRIGHT = + "Copyright (c) 1998-2001 Xerox Corporation, " + + "2002 Palo Alto Research Center, Incorporated. All rights reserved."; + + /** name of a filter property for the long build version (alpha) */ + public static final String BUILD_VERSION_LONG_NAME = "build.version.long"; + + /** name of a filter property for the short build version (alpha -> a, etc.) */ + public static final String BUILD_VERSION_SHORT_NAME = "build.version.short"; + + /** name of a filter property for the build time */ + public static final String BUILD_TIME_NAME = "build.time"; + + /** name of a filter property for the build date */ + public static final String BUILD_DATE_NAME = "build.date"; + + /** lazily and manually generate properties */ + public static Properties getFilterProperties(long time, String longVersion) { + if (time < 1) { + time = System.currentTimeMillis(); + } + if ((null == longVersion) || (0 == longVersion.length())) { + longVersion = System.getProperty( + BuildSpec.SYSTEM_BUILD_VERSION_KEY, + BuildSpec.BUILD_VERSION_DEFAULT); + } + Properties filterProps = new Properties(); + + // build time and date XXX set in build script? + String timeString = time+"L"; + // XXX wrong date format - use Version.java template format? + String date = new SimpleDateFormat("MMMM d, yyyy").format(new Date()); + filterProps.setProperty(BUILD_TIME_NAME, timeString); + filterProps.setProperty(BUILD_DATE_NAME, date); + + // build version, short build version, and base build version + // 1.1alpha1, 1.1a1, and 1.1 + String key = BuildSpec.BUILD_VERSION_NAME; + String value = longVersion; + value = value.trim(); + filterProps.setProperty(key, value); + + key = BuildSpec.BUILD_VERSION_LONG_NAME; + filterProps.setProperty(key, value); + + if (!BuildSpec.BUILD_VERSION_DEFAULT.equals(value)) { + value = Util.shortVersion(value); + } + key = BuildSpec.BUILD_VERSION_SHORT_NAME; + filterProps.setProperty(key, value); + + key = BuildSpec.BUILD_VERSION_BASE_NAME; + if (!BuildSpec.BUILD_VERSION_DEFAULT.equals(value)) { + int MAX = value.length(); + for (int i = 0; i < MAX; i++) { + char c = value.charAt(i); + if ((c != '.') && ((c < '0') || (c > '9'))) { + value = value.substring(0,i); + break; + } + } + } + filterProps.setProperty(key, value); + + // company name, copyright XXX fix company name + key = BuildSpec.COMPANY_NAME; + value = System.getProperty(key, BuildSpec.COMPANY_NAME_DEFAULT); + filterProps.setProperty(key, value); + filterProps.setProperty(BuildSpec.COPYRIGHT_NAME, BuildSpec.COPYRIGHT); + + return filterProps; + } + + // shared + public File baseDir; + public File moduleDir; + public File jarDir; + public File tempDir; + public File stagingDir; + public String buildConfig; + public String version; + public boolean rebuild; + public boolean trimTesting; + public boolean assembleAll; + public boolean failonerror; + public boolean verbose; + + // building products + public File productDir; + public boolean createInstaller; + public File distDir; + + // building modules + public String module; + + public String toString() { // XXX better + if (null != productDir) { + return "product " + productDir.getName(); + } else { + return "module " + moduleDir.getName(); + } + } +} + diff --git a/build/src/org/aspectj/internal/tools/build/Builder.java b/build/src/org/aspectj/internal/tools/build/Builder.java new file mode 100644 index 000000000..19c2ecb4b --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Builder.java @@ -0,0 +1,497 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import org.apache.tools.ant.BuildException; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Properties; + +/** + * Template class to build (eclipse) modules (and, weakly, products), + * including any required modules. + * When building modules, this assumes: + * <ul> + * <li>the name of the module is the base name of the module directory</li> + * <li>all module directories are in the same base (workspace) directory</li> + * <li>the name of the target module jar is {moduleName}.jar</li> + * <li>a module directory contains a <code>.classpath</code> file with + * (currently line-parseable) entries per Eclipse (XML) conventions</li> + * <li><code>Builder.RESOURCE_PATTERN</code> + * identifies all resources to copy to output.</li> + * <li>This can safely trim test-related code: + * <ul> + * <li>source directories named "testsrc"</li> + * <li>libraries named "junit.jar"</li> + * <li>required modules whose names start with "testing"</li> + * </ul> + * <li>A file <code>{moduleDir}/{moduleName}.properties</code> + * is a property file possibly + * containing entries defining requirements to be merged with the output jar + * (deprecated mechanism - use assembleAll or products)</li> + * </ul> + * This currently provides no control over the compile or assembly process, + * but clients can harvest <code>{moduleDir}/bin</code> directories to re-use + * the results of eclipse compiles. + * <p> + * When building products, this assumes: + * <ul> + * <li>the installer-resources directory is a peer of the products directory, + * itself the parent of the particular product directory.</li> + * <li>the dist, jar, product, and base (module) directory are set</li> + * <li>the product distribution consists of all (and only) the files + * in the dist sub-directory of the product directory</li> + * <li>files in the dist sub-directory that are empty and end with .jar + * represent modules to build, either as named or through aliases + * known here.</li> + * <li>When assembling the distribution, all non-binary files are to + * be filtered.<li> + * <li>the name of the product installer is aspectj-{productName}-{version}.jar, + * where {productName} is the base name of the product directory</li> + * </ul> + * <p> + * When run using main(String[]), all relevant Ant libraries and properties + * must be defined. + * <p> + * Written to compile standalone. Refactor if using utils, bridge, etc. + */ +public abstract class Builder { + + /** + * This has only weak forms for build instructions needed: + * - resource pattern + * - compiler selection and control + * + * Both assumed and generated paths are scattered; + * see XXXNameLiteral and XXXFileLiteral. + */ + + public static final String RESOURCE_PATTERN + = "**/*.txt,**/*.rsc,**/*.gif,**/*.properties"; + + public static final String BINARY_SOURCE_PATTERN + = "**/*.rsc,**/*.gif,**/*.jar,**/*.zip"; + + public static final String ALL_PATTERN = "**/*"; + + /** enable copy filter semantics */ + protected static final boolean FILTER_ON = true; + + /** disable copy filter semantics */ + protected static final boolean FILTER_OFF = false; + + protected final Messager handler; + protected boolean buildingEnabled; + + private final File tempDir; + private final ArrayList tempFiles; + private final boolean useEclipseCompiles; + + protected boolean verbose; + protected Properties filterProps; + protected boolean filterSetup; + + + protected Builder(File tempDir, boolean useEclipseCompiles, + Messager handler) { + Util.iaxIfNull(handler, "handler"); + this.useEclipseCompiles = useEclipseCompiles; + this.handler = handler; + this.tempFiles = new ArrayList(); + if ((null == tempDir) || !tempDir.canWrite() || !tempDir.isDirectory()) { + this.tempDir = Util.makeTempDir("Builder"); + } else { + this.tempDir = tempDir; + } + buildingEnabled = true; + } + + /** tell builder to stop or that it's ok to run */ + public void setBuildingEnabled(boolean enabled) { + buildingEnabled = enabled; + // XXX support user cancels in eclipse... + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public boolean build(BuildSpec buildSpec) { + if (!buildingEnabled) { + return false; + } + + if (null == buildSpec.productDir) { // ensure module properties + // derive moduleDir from baseDir + module + if (null == buildSpec.moduleDir) { + if (null == buildSpec.baseDir) { + throw new BuildException("require baseDir or moduleDir"); + } else if (null == buildSpec.module) { + throw new BuildException("require module with baseDir"); + } else { + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + buildSpec.moduleDir = new File(buildSpec.baseDir, buildSpec.module); + } + } else if (null == buildSpec.baseDir) { // derive baseDir from moduleDir parent + buildSpec.baseDir = buildSpec.moduleDir.getParentFile(); // rule: base is parent + if (null == buildSpec.baseDir) { + buildSpec.baseDir = new File("."); // user.home? + } + } + Util.iaxIfNotCanReadDir(buildSpec.moduleDir, "moduleDir"); + + if (null == buildSpec.module) { // derive module name from directory + buildSpec.module = buildSpec.moduleDir.getName(); + if (null == buildSpec.module) { + throw new BuildException("no name, even from " + buildSpec.moduleDir); + } + } + } + + if (null != buildSpec.productDir) { + return buildProduct(buildSpec); + } + if (buildSpec.trimTesting && (-1 != buildSpec.module.indexOf("testing"))) { // XXXNameLiteral + String warning = "Warning - cannot trimTesting for testing modules: "; + handler.log(warning + buildSpec.module); + } + Messager handler = new Messager(); + Modules modules = new Modules(buildSpec.baseDir, buildSpec.jarDir, buildSpec.trimTesting, handler); + + final Module moduleToBuild = modules.getModule(buildSpec.module); + ArrayList errors = new ArrayList(); + try { + return buildAll( + moduleToBuild, + errors, + buildSpec.rebuild, + buildSpec.assembleAll); + } finally { + if (0 < errors.size()) { + String label = "error building " + buildSpec + ": "; + for (Iterator iter = errors.iterator(); iter.hasNext();) { + handler.error(label + iter.next()); + } + } + } + } + + /** + * Clean up any temporary files, etc. after build completes + */ + public boolean cleanup() { + boolean noErr = true; + for (ListIterator iter = tempFiles.listIterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (!Util.deleteContents(file) || !file.delete()) { + if (noErr) { + noErr = false; + } + handler.log("unable to clean up " + file); + } + } + return noErr; + } + + /** + * Build a module with all antecedants. + * @param module the Module to build + * @param errors the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected boolean buildAll(Module module, List errors, boolean rebuild, boolean assembleAll) { + String[] buildList = getAntecedantModuleNames(module, rebuild); + if ((null != buildList) && (0 < buildList.length)) { + final Modules modules = module.getModules(); + final Messager handler = this.handler; + final boolean log = (verbose && (null != handler)); + final boolean verbose = this.verbose; + if (log) { + handler.log("modules to build: " + Arrays.asList(buildList)); + } + for (int i = 0; i < buildList.length; i++) { + + if (!buildingEnabled) { + return false; + } + String modName = buildList[i]; + if (log) { + handler.log("building " + modName); + } + Module next = modules.getModule(modName); + if (!buildOnly(next, errors)) { + return false; + } + } + } + if (assembleAll && !assembleAll(module, handler)) { + return false; + } + return true; + } + + /** + * Build a module but no antecedants. + * @param module the Module to build + * @param errors the List sink for errors, if any + * @return false after successful build, when module jar should exist + */ + protected boolean buildOnly(Module module, List errors) { + if (!buildingEnabled) { + return false; + } + File classesDir = useEclipseCompiles + ? new File(module.moduleDir, "bin") // XXXFileLiteral + : new File(tempDir, "classes-" + System.currentTimeMillis()); + if (verbose) { + handler.log("buildOnly " + module); + } + try { + return (useEclipseCompiles || compile(module, classesDir, errors)) + && assemble(module, classesDir, errors); + } finally { + if (!useEclipseCompiles + && (!Util.deleteContents(classesDir) || !classesDir.delete())) { + errors.add("unable to delete " + classesDir); + } + } + } + + /** + * Register temporary file or directory to be deleted when + * the build is complete, even if an Exception is thrown. + */ + protected void addTempFile(File tempFile) { + if (null != tempFile) { + tempFiles.add(tempFile); + } + } + /** + * Build product by discovering any modules to build, + * building those, assembling the product distribution, + * and optionally creating an installer for it. + * @return true on success + */ + protected boolean buildProduct(BuildSpec buildSpec) + throws BuildException { + Util.iaxIfNull(buildSpec, "buildSpec"); + // XXX if installer and not out of date, do not rebuild unless rebuild set + + if (!buildSpec.trimTesting) { + buildSpec.trimTesting = true; + handler.log("testing trimmed for " + buildSpec); + } + Util.iaxIfNotCanReadDir(buildSpec.productDir, "productDir"); + Util.iaxIfNotCanReadDir(buildSpec.baseDir, "baseDir"); + Util.iaxIfNotCanWriteDir(buildSpec.distDir, "distDir"); + + // ---- discover modules to build, and build them + Modules modules = new Modules( + buildSpec.baseDir, + buildSpec.jarDir, + buildSpec.trimTesting, + handler); + ProductModule[] productModules = discoverModules(buildSpec.productDir, modules); + for (int i = 0; i < productModules.length; i++) { + if (buildSpec.verbose) { + handler.log("building product module " + productModules[i]); + } + if (!buildProductModule(productModules[i])) { + return false; + } + } + if (buildSpec.verbose) { + handler.log("assembling product module for " + buildSpec); + } + + // ---- assemble product distribution + final String productName = buildSpec.productDir.getName(); + final File targDir = new File(buildSpec.distDir, productName); + final String targDirPath = targDir.getPath(); + if (targDir.canWrite()) { + Util.deleteContents(targDir); + } + + if (!targDir.canWrite() && !targDir.mkdirs()) { + if (buildSpec.verbose) { + handler.log("unable to create " + targDir); + } + return false; + } + // filter-copy everything but the binaries + File distDir = new File(buildSpec.productDir, "dist"); // XXXFileLiteral + String excludes = Builder.BINARY_SOURCE_PATTERN; + String includes = Builder.ALL_PATTERN; + if (!copyFiles(distDir, targDir, includes, excludes, FILTER_ON)) { + return false; + } + + // copy binaries (but not module flag files) + excludes = null; + { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < productModules.length; i++) { + if (0 < buf.length()) { + buf.append(","); + } + buf.append(productModules[i].relativePath); + } + if (0 < buf.length()) { + excludes = buf.toString(); + } + } + includes = Builder.BINARY_SOURCE_PATTERN; + if (!copyFiles(distDir, targDir, includes, excludes, FILTER_OFF)) { + return false; + } + + // copy binaries associated with module flag files + for (int i = 0; i < productModules.length; i++) { + ProductModule product = productModules[i]; + String targPath = targDirPath + "/" + product.relativePath; + File jarFile = (product.assembleAll + ? product.module.getAssembledJar() + : product.module.getModuleJar() ); + copyFile(jarFile, new File(targPath), FILTER_OFF); + } + handler.log("created product in " + targDir); + + // ---- create installer + if (buildSpec.createInstaller) { + return buildInstaller(buildSpec, targDirPath); + } else { + return true; + } + } + + protected boolean buildProductModule(ProductModule module) { + boolean noRebuild = false; + ArrayList errors = new ArrayList(); + try { + return buildAll(module.module, errors, noRebuild, module.assembleAll); + } finally { + for (Iterator iter = errors.iterator(); iter.hasNext();) { + handler.error("error building " + module + ": " + iter.next()); + } + } + } + + /** + * Discover any modules that might need to be built + * in order to assemble the product distribution. + * This interprets empty .jar files as module deliverables. + */ + protected ProductModule[] discoverModules(File productDir, Modules modules) { + final ArrayList found = new ArrayList(); + FileFilter filter = new FileFilter() { + public boolean accept(File file) { + if ((null != file) + && file.canRead() + && file.getPath().endsWith(".jar") // XXXFileLiteral + && (0l == file.length())) { + found.add(file); + } + return true; + } + }; + Util.visitFiles(productDir, filter); + ArrayList results = new ArrayList(); + for (Iterator iter = found.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + String jarName = moduleAliasFor(file.getName().toLowerCase()); + if (jarName.endsWith(".jar") || jarName.endsWith(".zip")) { // XXXFileLiteral + jarName = jarName.substring(0, jarName.length()-4); + } else { + handler.log("can only replace .[jar|zip]: " + file); // XXX error? + } + boolean assembleAll = jarName.endsWith("-all"); // XXXFileLiteral + String name = (!assembleAll ? jarName : jarName.substring(0, jarName.length()-4)); + Module module = modules.getModule(name); + if (null == module) { + handler.log("unable to find module for " + file); + } else { + results.add(new ProductModule(productDir, file, module, assembleAll)); + } + } + return (ProductModule[]) results.toArray(new ProductModule[0]); + } + + /** + * Map delivered-jar name to created-module name + * @param jarName the String (lowercased) of the jar/zip to map + */ + protected String moduleAliasFor(String jarName) { + if ("aspectjtools.jar".equals(jarName)) { // XXXFileLiteral + return "ajbrowser-all.jar"; + } else if ("aspectjrt.jar".equals(jarName)) { + return "runtime.jar"; + } else { + return jarName; + } + } + + /** + * @return String[] names of modules to build for this module + */ + abstract protected String[] getAntecedantModuleNames(Module toBuild, boolean rebuild); + + /** + * Compile module classes to classesDir, saving String errors. + */ + abstract protected boolean compile(Module module, File classesDir, List errors); + + /** + * Assemble the module distribution from the classesDir, saving String errors. + */ + abstract protected boolean assemble(Module module, File classesDir, List errors); + + /** + * Assemble the module distribution from the classesDir and all antecendants, + * saving String errors. + */ + abstract protected boolean assembleAll(Module module, Messager handler); + + /** + * Generate the installer for this product to targDirPath + */ + abstract protected boolean buildInstaller(BuildSpec buildSpec, String targDirPath); + + /** + * Copy fromFile to toFile, optionally filtering contents + */ + abstract protected boolean copyFile(File fromFile, File toFile, boolean filter); + + /** + * Copy toDir any fromDir included files without any exluded files, + * optionally filtering contents. + * @param fromDir File dir to read from - error if not readable + * @param toDir File dir to write to - error if not writable + * @param included String Ant pattern of included files (if null, include all) + * @param excluded String Ant pattern of excluded files (if null, exclude none) + * @param filter if FILTER_ON, then filter file contents using global token/value pairs + */ + abstract protected boolean copyFiles(File fromDir, File toDir, String included, String excluded, boolean filter); +} + + + diff --git a/build/src/org/aspectj/internal/tools/build/Messager.java b/build/src/org/aspectj/internal/tools/build/Messager.java new file mode 100644 index 000000000..c7b12d4a9 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Messager.java @@ -0,0 +1,43 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +/** logging stub XXX replace */ +public class Messager { + public Messager() { + } + public boolean log(String s) { + System.out.println(s); + return true; + } + + public boolean error(String s) { + System.out.println(s); + return true; + } + + public boolean logException(String context, Throwable thrown) { + System.err.println(context); + thrown.printStackTrace(System.err); + return true; + } +} + + + + + + + diff --git a/build/src/org/aspectj/internal/tools/build/Module.java b/build/src/org/aspectj/internal/tools/build/Module.java new file mode 100644 index 000000000..9c6022d18 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Module.java @@ -0,0 +1,501 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * This represents an (eclipse) build module/unit + * used by a Builder to compile classes + * and/or assemble zip file + * of classes, optionally with all antecedants. + * This implementation infers attributes from two + * files in the module directory: + * <ul> + * <li>an Eclipse project <code>.classpath</code> file + * containing required libraries and modules + * (collectively, "antecedants") + * </li> + * <li>a file <code>{moduleName}.mf.txt</code> is taken as + * the manifest of any .jar file produced, after filtering. + * </li> + * </ul> + * + * @see Builder + * @see Modules#getModule(String) + */ +public class Module { + + /** @return true if file is null or cannot be read or was + * last modified after time + */ + private static boolean outOfDate(long time, File file) { + return ((null == file) + || !file.canRead() + || (file.lastModified() > time)); + } + + /** @return all source files under srcDir */ + private static Iterator sourceFiles(File srcDir) { + ArrayList result = new ArrayList(); + sourceFiles(srcDir, result); + return result.iterator(); + } + + private static void sourceFiles(File srcDir, List result) { + if ((null == srcDir) || !srcDir.canRead() || !srcDir.isDirectory()) { + return; + } + File[] files = srcDir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + sourceFiles(files[i], result); + } else if (isSourceFile(files[i])) { + result.add(files[i]); + } + } + } + + /** + * Recursively find antecedant jars. + * @see findKnownJarAntecedants() + */ + private static void doFindKnownJarAntecedants(Module module, ArrayList known) { + Util.iaxIfNull(module, "module"); + Util.iaxIfNull(known, "known"); + + for (Iterator iter = module.getLibJars().iterator(); iter.hasNext();) { + File libJar = (File) iter.next(); + if (!skipLibraryJarAntecedant(libJar) + && !known.contains(libJar)) { // XXX what if same referent, diff path... + known.add(libJar); + } + } + for (Iterator iter = module.getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + File requiredJar = required.getModuleJar(); + if (!known.contains(requiredJar)) { + known.add(requiredJar); + doFindKnownJarAntecedants(required, known); + } + } + } + + /** XXX gack explicitly skip Ant */ + private static boolean skipLibraryJarAntecedant(File libJar) { + if (null == libJar) { + return true; + } + String path = libJar.getPath().replace('\\', '/'); + return (-1 == path.indexOf("/lib/ant/lib/")); + } + + /**@return true if this is a source file */ + private static boolean isSourceFile(File file) { + String path = file.getPath(); + return (path.endsWith(".java") || path.endsWith(".aj")); // XXXFileLiteral + } + + public final boolean valid; + + public final File moduleDir; + + public final String name; + + /** reference back to collection for creating required modules */ + final Modules modules; + + /** path to output jar - may not exist */ + private final File moduleJar; + + /** path to fully-assembed jar - may not exist */ + private final File assembledJar; + + /** File list of library jars */ + private final List libJars; + + /** File list of source directories */ + private final List srcDirs; + + /** properties from the modules {name}.properties file */ + private final Properties properties; + + /** Module list of required modules */ + private final List required; + + /** List of File that are newer than moduleJar. Null until requested */ + //private List newerFiles; + /** true if this has been found to be out of date */ + private boolean outOfDate; + + /** true if we have calculated whether this is out of date */ + private boolean outOfDateSet; + + /** if true, trim testing-related source directories, modules, and libraries */ + private final boolean trimTesting; + + /** logger */ + private final Messager messager; + + Module(File moduleDir, + File jarDir, + String name, + Modules modules, + boolean trimTesting, + Messager messager) { + Util.iaxIfNotCanReadDir(moduleDir, "moduleDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(name, "name"); + Util.iaxIfNull(modules, "modules"); + this.moduleDir = moduleDir; + this.trimTesting = trimTesting; + this.libJars = new ArrayList(); + this.required = new ArrayList(); + this.srcDirs = new ArrayList(); + this.properties = new Properties(); + this.name = name; + this.modules = modules; + this.messager = messager; + this.moduleJar = new File(jarDir, name + ".jar"); + this.assembledJar = new File(jarDir, name + "-all.jar"); + valid = init(); + } + + /** @return path to output jar - may not exist */ + public File getModuleJar() { + return moduleJar; + } + + /** @return path to output assembled jar - may not exist */ + public File getAssembledJar() { + return assembledJar; + } + + /** @return unmodifiable List of required modules String names*/ + public List getRequired() { + return Collections.unmodifiableList(required); + } + + /** @return unmodifiable list of required library files, guaranteed readable */ + public List getLibJars() { + return Collections.unmodifiableList(libJars); + } + + /** @return unmodifiable list of source directories, guaranteed readable */ + public List getSrcDirs() { + return Collections.unmodifiableList(srcDirs); + } + + /** @return Modules registry of known modules, including this one */ + public Modules getModules() { + return modules; + } + + /** @return List of File jar paths to be merged into module-dist */ + public List getMerges() { + String value = properties.getProperty(name + ".merges"); + if ((null == value) || (0 == value.length())) { + return Collections.EMPTY_LIST; + } + ArrayList result = new ArrayList(); + StringTokenizer st = new StringTokenizer(value); + while (st.hasMoreTokens()) { + result.addAll(findJarsBySuffix(st.nextToken())); + } + return result; + } + + + public void clearOutOfDate() { + outOfDate = false; + outOfDateSet = false; + } + + /** + * @param recalculate if true, then force recalculation + * @return true if the target jar for this module is older than + * any source files in a source directory + * or any required modules + * or any libraries + * or if any libraries or required modules are missing + */ + public boolean outOfDate(boolean recalculate) { + if (recalculate) { + outOfDateSet = false; + } + if (!outOfDateSet) { + outOfDate = false; + try { + if (!(moduleJar.exists() && moduleJar.canRead())) { + return outOfDate = true; + } + final long time = moduleJar.lastModified(); + File file; + for (Iterator iter = srcDirs.iterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + for (Iterator srcFiles = sourceFiles(srcDir); srcFiles.hasNext();) { + file = (File) srcFiles.next(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + } + // required modules + for (Iterator iter = getRequired().iterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + file = required.getModuleJar(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + // libraries + for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { + file = (File) iter.next(); + if (outOfDate(time, file)) { + return outOfDate = true; + } + } + } finally { + outOfDateSet = true; + } + } + return outOfDate; + } + /** + * Add any (File) library jar or (File) required module jar + * to the List known, if not added already. + */ + public ArrayList findKnownJarAntecedants() { + ArrayList result = new ArrayList(); + doFindKnownJarAntecedants(this, result); + return result; + } + + public String toString() { + return name; + } + + public String toLongString() { + return + "Module [name=" + + name + + ", srcDirs=" + + srcDirs + + ", required=" + + required + + ", moduleJar=" + + moduleJar + + ", libJars=" + + libJars + + "]"; + } + + private boolean init() { + return initClasspath() && initProperties() && reviewInit(); + } + + /** read eclipse .classpath file XXX line-oriented hack */ + private boolean initClasspath() { + // meaning testsrc directory, junit library, etc. + File file = new File(moduleDir, ".classpath"); // XXXFileLiteral + FileReader fin = null; + try { + fin = new FileReader(file); + BufferedReader reader = new BufferedReader(fin); + String line; + String lastKind = null; + while (null != (line = reader.readLine())) { + lastKind = parseLine(line, lastKind); + } + return (0 < srcDirs.size()); + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + } finally { + if (null != fin) { + try { fin.close(); } + catch (IOException e) {} // ignore + } + } + return false; + } + + /** @return true if any properties were read correctly */ + private boolean initProperties() { + File file = new File(moduleDir, name + ".properties"); // XXXFileLiteral + if (!Util.canReadFile(file)) { + return true; // no properties to read + } + FileInputStream fin = null; + try { + fin = new FileInputStream(file); + properties.load(fin); + return true; + } catch (IOException e) { + messager.logException("IOException reading " + file, e); + return false; + } finally { + if (null != fin) { + try { fin.close(); } + catch (IOException e) {} // ignore + } + } + } + + /** + * Post-process initialization. + * This implementation trims testing-related source + * directories, libraries, and modules if trimTesting is enabled/true. + * To build testing modules, trimTesting must be false. + * @return true if initialization post-processing worked + */ + protected boolean reviewInit() { + if (!trimTesting) { + return true; + } + try { + for (ListIterator iter = srcDirs.listIterator(); iter.hasNext();) { + File srcDir = (File) iter.next(); + String name = srcDir.getName(); + if ("testsrc".equals(name.toLowerCase())) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + for (ListIterator iter = libJars.listIterator(); iter.hasNext();) { + File libJar = (File) iter.next(); + String name = libJar.getName(); + if ("junit.jar".equals(name.toLowerCase())) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + for (ListIterator iter = required.listIterator(); iter.hasNext();) { + Module required = (Module) iter.next(); + String name = required.name; + // XXX testing-util only ? + if (name.toLowerCase().startsWith("testing")) { // XXXFileLiteral + iter.remove(); // XXX if verbose log + } + } + } catch (UnsupportedOperationException e) { + return false; // failed XXX log also if verbose + } + return true; + } + + private String parseLine(String line, String lastKind) { + if (null == line) { + return null; + } + String kind; + int loc = line.indexOf("kind=\""); + if ((-1 == loc) || (loc + 9 > line.length())) { + // no kind string - fail unless have lastKind + if (null == lastKind) { + return null; + } else { + kind = lastKind; + } + } else { // have kind string - get kind + loc += 6; // past kind=" + kind = line.substring(loc, loc+3); + } + + // now look for value + loc = line.indexOf("path=\""); + if (-1 == loc) { // no value - return lastKind + return kind; + } + loc += 6; // past path=" + int end = line.indexOf("\"", loc); + if (-1 == end) { + throw new Error("unterminated path in " + line); + } + final String path = line.substring(loc, end); + + if ("src".equals(kind)) { + if (path.startsWith("/")) { // module + String moduleName = path.substring(1); + Module req = modules.getModule(moduleName); + if (null != req) { + required.add(req); + } else { + messager.error("unable to create required module: " + moduleName); + } + } else { // src dir + File srcDir = new File(moduleDir, path); + if (srcDir.canRead() && srcDir.isDirectory()) { + srcDirs.add(srcDir); + } else { + messager.error("not a src dir: " + srcDir); + } + } + } else if ("lib".equals(kind)) { + String libPath = path.startsWith("/") + ? modules.baseDir.getAbsolutePath() + path + : path; + File libJar = new File(libPath); + if (libJar.canRead() && libJar.isFile()) { + libJars.add(libJar); + } else { + messager.error("no such library jar " + libJar + " from " + line); + } + } else if ("var".equals(kind)) { + if (!"JRE_LIB".equals(path)) { + messager.log("cannot handle var yet: " + line); + } + } else if ("con".equals(kind)) { + messager.log("cannot handle con yet: " + line); + } else if ("out".equals(kind)) { + // ignore output entries + } else { + messager.log("unrecognized kind " + kind + " in " + line); + } + return null; + } + + /** @return List of File of any module or library jar ending with suffix */ + private ArrayList findJarsBySuffix(String suffix) { + ArrayList result = new ArrayList(); + if (null != suffix) { + // library jars + for (Iterator iter = getLibJars().iterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + // module jars + for (Iterator iter = getRequired().iterator(); iter.hasNext();) { + Module module = (Module) iter.next(); + File file = module.getModuleJar(); + if (file.getPath().endsWith(suffix)) { + result.add(file); + } + } + } + return result; + } +} + diff --git a/build/src/org/aspectj/internal/tools/build/Modules.java b/build/src/org/aspectj/internal/tools/build/Modules.java new file mode 100644 index 000000000..55b2fa800 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Modules.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.util.Hashtable; + +/** + * Registration and factory for modules + * @see Module + * @see Builder + */ +public class Modules { + + private final Hashtable modules = new Hashtable(); + public final File baseDir; + public final File jarDir; + private final Messager handler; + public final boolean trimTesting; + + public Modules(File baseDir, File jarDir, boolean trimTesting, Messager handler) { + this.baseDir = baseDir; + this.jarDir = jarDir; + this.handler = handler; + this.trimTesting = trimTesting; + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNotCanReadDir(jarDir, "jarDir"); + Util.iaxIfNull(handler, "handler"); + } + + + /** + * Get module associated with name. + * @return fail if unable to find or create module {name}. + */ + public Module getModule(String name) { + if (null == name) { + return null; + } + Module result = (Module) modules.get(name); + if (null == result) { + File moduleDir = new File(baseDir, name); + if (!Util.canReadDir(moduleDir)) { + handler.error("not a module: " + name); + } else { + result = new Module(moduleDir, jarDir, name, this, trimTesting, handler); + if (result.valid) { + modules.put(name, result); + } else { + handler.error("invalid module: " + result.toLongString()); + } + } + } + return result; + } +}
\ No newline at end of file diff --git a/build/src/org/aspectj/internal/tools/build/ProductModule.java b/build/src/org/aspectj/internal/tools/build/ProductModule.java new file mode 100644 index 000000000..90e6f3879 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/ProductModule.java @@ -0,0 +1,70 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ +package org.aspectj.internal.tools.build; + +import java.io.File; + +/** + * Struct associating module with target product distribution jar + * and assembly instructions. + * When building product distributions, a zero-length jar file + * in the dist directory may signify a module to be built, + * renamed, and included in the distribution. + */ +public class ProductModule { + /** name of distribution directory in product directory */ + private static final String DIST = "dist"; + + /** top-level product directory being produced */ + public final File productDir; + + /** path to file in distribution template dir for this module jar */ + public final File replaceFile; + + /** relative path within distribution of this product module jar */ + public final String relativePath; + + /** the module jar is the file to replace */ + public final Module module; + + /** if true, assemble all when building module */ + public final boolean assembleAll; + + public ProductModule(File productDir, File replaceFile, Module module, boolean assembleAll) { + this.replaceFile = replaceFile; + this.module = module; + this.productDir = productDir; + this.assembleAll = assembleAll; + Util.iaxIfNull(module, "module"); + Util.iaxIfNotCanReadDir(productDir, "productDir"); + Util.iaxIfNotCanReadFile(replaceFile, "replaceFile"); + String productDirPath = productDir.getAbsolutePath(); + String replaceFilePath = replaceFile.getAbsolutePath(); + if (!replaceFilePath.startsWith(productDirPath)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + + productDirPath + + "\""; + throw new IllegalArgumentException(m); + } + replaceFilePath = replaceFilePath.substring(1+productDirPath.length()); + if (!replaceFilePath.startsWith(DIST)) { + String m = "\"" + replaceFilePath + + "\" does not start with \"" + DIST + "\""; + throw new IllegalArgumentException(m); + } + relativePath = replaceFilePath.substring(1 + DIST.length()); + } + public String toString() { + return "" + module + " for " + productDir; + } +} diff --git a/build/src/org/aspectj/internal/tools/build/Util.java b/build/src/org/aspectj/internal/tools/build/Util.java new file mode 100644 index 000000000..27ca9e1b4 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/Util.java @@ -0,0 +1,174 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.internal.tools.build; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Build-only utilities. + * Many mirror utils module APIs. + */ +public class Util { + + /** + * Map version in long form to short, + * e.g., replacing "alpha" with "a" + */ + public static String shortVersion(String version) { + version = Util.replace(version, "alpha", "a"); + version = Util.replace(version, "beta", "b"); + version = Util.replace(version, "candidate", "rc"); + version = Util.replace(version, "development", "d"); + version = Util.replace(version, "dev", "d"); + return version; + } + + /** + * Replace any instances of {replace} in {input} with {with}. + * @param input the String to search/replace + * @param replace the String to search for in input + * @param with the String to replace with in input + * @return input if it has no replace, otherwise a new String + */ + public static String replace(String input, String replace, String with) { + int loc = input.indexOf(replace); + if (-1 != loc) { + String result = input.substring(0, loc); + result += with; + int start = loc + replace.length(); + if (start < input.length()) { + result += input.substring(start); + } + input = result; + } + return input; + } + + /** @return false if filter returned false for any file in baseDir subtree */ + public static boolean visitFiles(File baseDir, FileFilter filter) { + Util.iaxIfNotCanReadDir(baseDir, "baseDir"); + Util.iaxIfNull(filter, "filter"); + File[] files = baseDir.listFiles(); + boolean passed = true; + for (int i = 0; passed && (i < files.length); i++) { + passed = files[i].isDirectory() + ? visitFiles(files[i], filter) + : filter.accept(files[i]); + } + return passed; + } + + /** @throws IllegalArgumentException if cannot read dir */ + public static void iaxIfNotCanReadDir(File dir, String name) { + if (!canReadDir(dir)) { + throw new IllegalArgumentException(name + " dir not readable: " + dir); + } + } + + /** @throws IllegalArgumentException if cannot read file */ + public static void iaxIfNotCanReadFile(File file, String name) { + if (!canReadFile(file)) { + throw new IllegalArgumentException(name + " file not readable: " + file); + } + } + + /** @throws IllegalArgumentException if cannot write dir */ + public static void iaxIfNotCanWriteDir(File dir, String name) { + if (!canWriteDir(dir)) { + throw new IllegalArgumentException(name + " dir not writeable: " + dir); + } + } + + /** @throws IllegalArgumentException if input is null */ + public static void iaxIfNull(Object input, String name) { + if (null == input) { + throw new IllegalArgumentException("null " + name); + } + } + + /** render exception to String */ + public static String renderException(Throwable thrown) { + if (null == thrown) { + return "(Throwable) null"; + } + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + pw.println(thrown.getMessage()); + thrown.printStackTrace(pw); + pw.flush(); + return sw.getBuffer().toString(); + } + + /** @return true if dir is a writable directory */ + public static boolean canWriteDir(File dir) { + return (null != dir) && dir.canWrite() && dir.isDirectory(); + } + + /** @return true if dir is a readable directory */ + public static boolean canReadDir(File dir) { + return (null != dir) && dir.canRead() && dir.isDirectory(); + } + + /** @return true if dir is a readable file */ + public static boolean canReadFile(File file) { + return (null != file) && file.canRead() && file.isFile(); + } + + /** + * Delete contents of directory. + * The directory itself is not deleted. + * @param dir the File directory whose contents should be deleted. + * @return true if all contents of dir were deleted + */ + public static boolean deleteContents(File dir) { + if ((null == dir) || !dir.canWrite()) { + return false; + } else if (dir.isDirectory()) { + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + if (!deleteContents(files[i]) || !files[i].delete()) { + return false; + } + } + } + return true; + } + + /** @return File temporary directory with the given prefix */ + public static File makeTempDir(String prefix) { + if (null == prefix) { + prefix = "tempDir"; + } + File tempFile = null; + for (int i = 0; i < 10; i++) { + try { + tempFile = File.createTempFile(prefix,"tmp"); + tempFile.delete(); + if (tempFile.mkdirs()) { + break; + } + tempFile = null; + } catch (IOException e) { + } + } + return tempFile; + } + +} + diff --git a/build/src/org/aspectj/internal/tools/build/package.html b/build/src/org/aspectj/internal/tools/build/package.html new file mode 100644 index 000000000..3fa443812 --- /dev/null +++ b/build/src/org/aspectj/internal/tools/build/package.html @@ -0,0 +1,12 @@ +<html> +<!-- todo +- backed off on doing product installer builds directly; + the one installer is now built using Ant. + +- +--> +<body> +The build taskdef relies on the classes in this package for +behavior independent of Ant. +</body> +</html> diff --git a/build/testsrc/BuildModuleTests.java b/build/testsrc/BuildModuleTests.java new file mode 100644 index 000000000..651b061a6 --- /dev/null +++ b/build/testsrc/BuildModuleTests.java @@ -0,0 +1,143 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +// default package + +import org.aspectj.internal.tools.ant.taskdefs.Checklics; +import org.aspectj.internal.build.BuildModuleTest; + +import java.io.File; + +import junit.framework.*; + +public class BuildModuleTests extends TestCase { + + /** if true, then replace old headers with new first */ + private static final boolean replacing = false; // XXX never to enable again... + + /** if any replace failed, halt all */ + private static boolean replaceFailed = false; + + private static final String BASE_DIR = "../"; + private static final String[] JDT_SOURCE_DIRS = new String[] + {"antadapter", "batch", "codeassist", "compiler", "dom", "eval", "formatter", + "model", "search" }; + + public static Test suite() { + TestSuite suite = new TestSuite("Build module tests"); + suite.addTestSuite(BuildModuleTests.class); + suite.addTestSuite(BuildModuleTest.class); + return suite; + } + + /** @return String tag of license if not default */ + public static String getLicense(String module) { + if ("org.eclipse.jdt.core".equals(module)) { + return Checklics.CPL_IBM_PARC_TAG; + } + return null; + } + + public BuildModuleTests(String name) { super(name); } + + public void testLicense_ajbrowser() { + checkLicense("ajbrowser"); + } + public void testLicense_ajde() { + checkLicense("ajde"); + } + public void testLicense_asm() { + checkLicense("asm"); + } + public void testLicense_bcweaver() { + String module = "bcweaver"; + checkSourceDirectory("../" + module + "/src", module); + checkSourceDirectory("../" + module + "/testsrc/org", module); + } + public void testLicense_bridge() { + checkLicense("bridge"); + } + public void testLicense_build() { + checkLicense("build"); + } + public void testLicense_jbuilder() { + checkLicense("jbuilder"); + } + public void testLicense_netbeans() { + checkLicense("netbeans"); + } + public void testLicense_org_aspectj_ajdt_core() { + checkLicense("org.aspectj.ajdt.core"); + } + public void testLicense_org_eclipse_jdt_core() { + final String mod = "org.eclipse.jdt.core"; + final String pre = BASE_DIR + mod + "/"; + for (int i = 0; i < JDT_SOURCE_DIRS.length; i++) { + checkSourceDirectory(pre + JDT_SOURCE_DIRS[i], mod); + } + } + + public void testLicense_runtime() { + checkLicense("runtime"); + } + public void testLicense_taskdefs() { + checkLicense("taskdefs"); + } + public void testLicense_testing() { + checkLicense("testing"); + } + public void testLicense_testing_drivers() { + checkLicense("testing-drivers"); + } + public void testLicense_testing_client() { + checkLicense("testing-client"); + } + public void testLicense_testing_util() { + checkLicense("testing-util"); + } + public void testLicense_util() { + checkLicense("util"); + } + + void checkLicense(String module) { + checkSourceDirectory("../" + module + "/src", module); + checkSourceDirectory("../" + module + "/testsrc", module); + } + + void checkSourceDirectory(String path, String module) { + File moduleDir = new File(path); + final String label = "source dir " + moduleDir + " (module " + module + ")"; + assertTrue(label, (moduleDir.exists() && moduleDir.isDirectory())); + String license = getLicense(module); + if (replacing) { + if (replacing) { + throw new Error("replacing done - code left for other replaces"); + } + assertTrue("aborting - replace failed", !replaceFailed); + // do the replace + int fails = Checklics.runDirect(moduleDir.getPath(), "replace-headers"); + replaceFailed = (0 != fails); + assertTrue(!replaceFailed); + license = Checklics.CPL_IBM_PARC_XEROX_TAG; + } + int fails = Checklics.runDirect(moduleDir.getPath(), license); + if (0 != fails) { + if (replacing) { + replaceFailed = true; + } + assertTrue(label + " fails", false); + } + } + +} diff --git a/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java b/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java new file mode 100644 index 000000000..6bee0a63f --- /dev/null +++ b/build/testsrc/org/aspectj/internal/build/BuildModuleTest.java @@ -0,0 +1,179 @@ +/* ******************************************************************* + * Copyright (c) 1999-2001 Xerox Corporation, + * 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.internal.build; + +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Java; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Commandline.Argument; +import org.aspectj.internal.tools.ant.taskdefs.BuildModule; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; + +import junit.framework.TestCase; +/** + * + */ +public class BuildModuleTest extends TestCase { + + private static final String SKIP_MESSAGE = + "Define \"run.build.tests\" as a system property to run tests to build "; + private static boolean delete(File file) { // XXX Util + if ((null == file) || !file.exists()) { + return true; + } + if (file.isFile()) { + return file.delete(); + } else { + File[] files = file.listFiles(); + boolean result = true; + for (int i = 0; i < files.length; i++) { + if (!BuildModuleTest.delete(files[i]) + && result) { + result = false; + } + } + return (file.delete() && result); + } + } + + ArrayList tempFiles = new ArrayList(); + boolean building; // must be enabled for tests to run + + public BuildModuleTest(String name) { + super(name); + building = (null != System.getProperty("run.build.tests")); + } + + protected void tearDown() throws Exception { + super.tearDown(); + for (Iterator iter = tempFiles.iterator(); iter.hasNext();) { + File file = (File) iter.next(); + if (!BuildModuleTest.delete(file)) { + System.err.println("warning: BuildModuleTest unable to delete " + file); + } + } + } + + public void testBuild() { + checkBuild("build", null, null); + } + public void testAsm() { + checkBuild("asm", null, null); + } + + public void testRuntime() { + checkBuild("runtime", null, null); + } + + public void testAjbrowser() { + checkBuild("ajbrowser", null, null); + } + + public void testAjdt() { + checkBuild("org.aspectj.ajdt.core", "org.aspectj.tools.ajc.Main", + new String[] { "-version" }); + } + + public void testAspectjtools() { + if (!building) { + System.err.println(SKIP_MESSAGE + "aspectjtools"); + return; + } + File baseDir = new File(".."); + File tempBuildDir = new File(baseDir, "aj-build"); + File distDir = new File(tempBuildDir, "dist"); + File jarDir = new File(tempBuildDir, "jars"); + assertTrue(distDir.canWrite() || distDir.mkdirs()); + File productDir = new File(baseDir.getPath() + "/build/products/tools"); + assertTrue(""+productDir, productDir.canRead()); + checkBuildProduct(productDir, baseDir, distDir, jarDir); + } + + void checkBuildProduct(File productDir, File baseDir, File distDir, File jarDir) { + if (!building) { + System.err.println(SKIP_MESSAGE + "product " + productDir); + return; + } + assertTrue(null != productDir); + assertTrue(productDir.canRead()); + + checkJavac(); + + BuildModule task = new BuildModule(); + Project project = new Project(); + task.setProject(project); + assertTrue(jarDir.canWrite() || jarDir.mkdirs()); + // XXX restore tempFiles.add(jarDir); + task.setJardir(new Path(project, jarDir.getAbsolutePath())); + task.setProductdir(new Path(project, productDir.getAbsolutePath())); + task.setBasedir(new Path(project, baseDir.getAbsolutePath())); + task.setDistdir(new Path(project, distDir.getAbsolutePath())); + task.setFailonerror(true); + //task.setVerbose(true); + task.setCreateinstaller(true); + task.execute(); + // now run installer and do product tests? + } + + void checkBuild(String module, String classname, String[] args) { + if (!building) { + System.err.println(SKIP_MESSAGE + "module " + module); + return; + } + assertTrue(null != module); + checkJavac(); + + BuildModule task = new BuildModule(); + Project project = new Project(); + task.setProject(project); + File jarDir = new File("tempJarDir"); + assertTrue(jarDir.canWrite() || jarDir.mkdirs()); + tempFiles.add(jarDir); + task.setModuledir(new Path(project, "../" + module)); + task.setJardir(new Path(project, jarDir.getAbsolutePath())); + task.setFailonerror(true); + task.execute(); + + if (null == classname) { + return; + } + + Java java = new Java(); + java.setProject(project); + File jar = new File(jarDir, module + ".jar"); + java.setClasspath(new Path(project, jar.getAbsolutePath())); + java.setClassname(classname); + for (int i = 0; i < args.length; i++) { + Argument arg = java.createArg(); + arg.setValue(args[i]); + } + java.execute(); + } + + void checkJavac() { + boolean result = false; + try { + result = (null != Class.forName("sun.tools.javac.Main")); + } catch (Throwable t) { + // ignore + } + if (! result) { + assertTrue("add tools.jar to the classpath for Ant's use of javac", false); + } + } + +} |