001/* Build documentation for Java and Actors 002 003 Copyright (c) 2006-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.vergil.actor; 029 030import java.io.File; 031import java.util.Iterator; 032import java.util.LinkedList; 033import java.util.List; 034 035import ptolemy.actor.gui.Configuration; 036import ptolemy.data.BooleanToken; 037import ptolemy.data.expr.Parameter; 038import ptolemy.data.type.BaseType; 039import ptolemy.kernel.util.Attribute; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.KernelException; 042import ptolemy.kernel.util.NameDuplicationException; 043import ptolemy.kernel.util.NamedObj; 044import ptolemy.kernel.util.StringAttribute; 045import ptolemy.util.ExecuteCommands; 046import ptolemy.util.FileUtilities; 047import ptolemy.util.StreamExec; 048import ptolemy.util.StringUtilities; 049 050/////////////////////////////////////////////////////////////////// 051//// DocBuilder 052 053/** Build Documentation for Java and Actors. 054 * 055 * <p>This class sets the commands that build the Java classes. 056 * 057 * @author Christopher Brooks 058 * @version $Id$ 059 * @since Ptolemy II 5.2 060 * @Pt.ProposedRating Yellow (eal) 061 * @Pt.AcceptedRating Yellow (eal) 062 */ 063public class DocBuilder extends Attribute { 064 065 // In principle, this class should be usable from both within a UI 066 // and without a UI. 067 068 /** Create a new instance of the DocBuilder. 069 * @param container The container. 070 * @param name The name of the code generator. 071 * @exception IllegalActionException If the super class throws the 072 * exception or error occurs when setting the file path. 073 * @exception NameDuplicationException If the super class throws the 074 * exception or an error occurs when setting the file path. 075 */ 076 public DocBuilder(NamedObj container, String name) 077 throws IllegalActionException, NameDuplicationException { 078 super(container, name); 079 cleanFirst = new Parameter(this, "cleanFirst"); 080 cleanFirst.setTypeEquals(BaseType.BOOLEAN); 081 082 cleanFirst.setExpression("true"); 083 } 084 085 /////////////////////////////////////////////////////////////////// 086 //// parameters //// 087 088 /** If true, then clean before building documentation. The default 089 * value is true because if a user is adding an actor, then the 090 * codeDoc/tree.html, codeDoc/ptolemy/actor/lib/Ramp.xml, 091 * and codeDoc/ptolemy/actor/lib/RampIdx.xml files might already 092 * exist. It is safer to force a clean each time because the 093 * makefile does not accurately capture the dependencies between 094 * .java sources and .html, .xml and Idx.xml files. 095 */ 096 public Parameter cleanFirst; 097 098 /////////////////////////////////////////////////////////////////// 099 //// public methods //// 100 101 /** Build the Java class and Actor documentation. 102 * The default is to run make in <code>$PTII/doc</code>. 103 * 104 * However, if the configuration set by {@link 105 * #setConfiguration(Configuration)} then the configuration is 106 * searched for a _docApplicationSpecializer parameter. If that 107 * parameter exists it is assumed to name a class that implements 108 * the {@link DocApplicationSpecializer} interface and the 109 * {@link DocApplicationSpecializer#buildCommands(ExecuteCommands)} 110 * method which returns the commands to invoke. 111 * 112 * @return The return value of the last subprocess that was executed. 113 * or -1 if no commands were executed. 114 * @exception IllegalActionException If there is a problem building 115 * the documentation. 116 */ 117 public int buildDocs() throws IllegalActionException { 118 if (_executeCommands == null) { 119 _executeCommands = new StreamExec(); 120 } 121 return _executeCommands(); 122 } 123 124 /** Get the command executor, which can be either non-graphical 125 * or graphical. The initial default is non-graphical, which 126 * means that stderr and stdout from subcommands is written 127 * to the console. 128 * @return executeCommands The subprocess command executor. 129 * @see #setExecuteCommands(ExecuteCommands) 130 */ 131 public ExecuteCommands getExecuteCommands() { 132 return _executeCommands; 133 } 134 135 /** Set the configuration. 136 * @param configuration The configuration in which we look up the 137 * _applicationName and _docApplicationSpecializer parameters. 138 */ 139 public void setConfiguration(Configuration configuration) { 140 _configuration = configuration; 141 } 142 143 /** Set the command executor, which can be either non-graphical 144 * or graphical. The initial default is non-graphical, which 145 * means that stderr and stdout from subcommands is written 146 * to the console. 147 * @param executeCommands The subprocess command executor. 148 * @see #getExecuteCommands() 149 */ 150 public void setExecuteCommands(ExecuteCommands executeCommands) { 151 _executeCommands = executeCommands; 152 } 153 154 /////////////////////////////////////////////////////////////////// 155 //// private methods //// 156 157 /** Return the command to compile ptII/doc/doclets/PtDoclet. 158 */ 159 private static String _compilePtDoclet(File ptII) { 160 String results = ""; 161 try { 162 String javaHome = StringUtilities.getProperty("java.home"); 163 if (javaHome != null && javaHome.length() > 1) { 164 javaHome = javaHome.replace('\\', '/'); 165 String toolsJarFileBase = "/../lib/tools.jar"; 166 File toolsJarFile = new File(javaHome + toolsJarFileBase); 167 if (toolsJarFile.exists()) { 168 results = "javac -classpath \"" + ptII + File.pathSeparator 169 + javaHome + toolsJarFileBase 170 + "\" doc/doclets/PtDoclet.java"; 171 } else { 172 if (StringUtilities.getProperty("os.name") 173 .equals("Mac OS X")) { 174 results = "javac -classpath \"" + ptII 175 + "\" doc/doclets/PtDoclet.java"; 176 } else { 177 results = "echo \"Warning: Failed to generate commands to compile " 178 + "ptII/doc/doclets/PtDoclet.java. The jar file tools.jar at " 179 + toolsJarFile.getCanonicalPath() 180 + " does not exist?\""; 181 } 182 } 183 } 184 } catch (Throwable throwable) { 185 results = "echo \"Warning, failed to generate command " 186 + "to compile ptII/doc/doclets/PtDoclet.java: " 187 + KernelException.stackTraceToString(throwable) + "\""; 188 189 } 190 return results; 191 } 192 193 /** Build the documentation. 194 * @return The return value of the last subprocess that was executed 195 * or -1 if no commands were executed. 196 */ 197 private int _executeCommands() throws IllegalActionException { 198 199 File ptII = new File( 200 StringUtilities.getProperty("ptolemy.ptII.dir") + "/doc"); 201 _executeCommands.setWorkingDirectory(ptII); 202 203 List commands = null; 204 // Search for a _docApplicationSpecializer in the configuration. 205 Parameter docApplicationSpecializerParameter = null; 206 if (_configuration != null) { 207 docApplicationSpecializerParameter = (Parameter) _configuration 208 .getAttribute("_docApplicationSpecializer", 209 Parameter.class); 210 } 211 if (docApplicationSpecializerParameter != null) { 212 String docApplicationSpecializerClassName = docApplicationSpecializerParameter 213 .getExpression(); 214 215 try { 216 Class docApplicationSpecializerClass = Class 217 .forName(docApplicationSpecializerClassName); 218 DocApplicationSpecializer docApplicationSpecializer = (DocApplicationSpecializer) docApplicationSpecializerClass 219 .newInstance(); 220 commands = docApplicationSpecializer 221 .buildCommands(_executeCommands); 222 } catch (Throwable throwable) { 223 throw new IllegalActionException( 224 "Failed to call doc application initializer " 225 + "class \"" 226 + docApplicationSpecializerClassName 227 + "\" buildCommands() method"); 228 } 229 } else { 230 commands = new LinkedList(); 231 String applicationName = null; 232 233 try { 234 StringAttribute applicationNameAttribute = (StringAttribute) _configuration 235 .getAttribute("_applicationName", 236 StringAttribute.class); 237 238 if (applicationNameAttribute != null) { 239 applicationName = applicationNameAttribute.getExpression(); 240 } 241 } catch (Throwable throwable) { 242 // Ignore and use the default applicationName: "", 243 // which means we look in doc.codeDoc. 244 } 245 246 // Windows users might not have the rm command. 247 if (((BooleanToken) cleanFirst.getToken()).booleanValue()) { 248 String codeDocDirectory = ptII + "/codeDoc"; 249 _executeCommands.updateStatusBar("Deleting the contents of \"" 250 + codeDocDirectory + "\"."); 251 if (!FileUtilities.deleteDirectory(codeDocDirectory)) { 252 _executeCommands.stderr( 253 "Warning: Could not delete some of the files in \"" 254 + codeDocDirectory + "\"."); 255 } 256 } 257 258 if (applicationName == null) { 259 File ptIImk = new File( 260 StringUtilities.getProperty("ptolemy.ptII.dir") 261 + "/mk/ptII.mk"); 262 // If the user has run configure, then we run make, 263 // otherwise we run the javadoc command. 264 if (ptIImk.exists()) { 265 commands.add("make codeDoc/js/index.html"); 266 commands.add("make codeDoc/tree.html"); 267 commands.add("make codeDoc/ptolemy/actor/lib/Ramp.xml"); 268 commands.add("make codeDoc/ptolemy/actor/lib/RampIdx.xml"); 269 } else { 270 ptII = new File( 271 StringUtilities.getProperty("ptolemy.ptII.dir")); 272 _executeCommands.setWorkingDirectory(ptII); 273 _executeCommands.updateStatusBar( 274 "When creating docs, warnings are ok."); 275 276 commands.add(_compilePtDoclet(ptII)); 277 278 if (FileUtilities.inPath("ant")) { 279 commands.add("ant jsdoc ptdoc"); 280 } else { 281 String message = "ant not found? jsdoc and ptdoc will not be run."; 282 _executeCommands.updateStatusBar(message); 283 System.err.println(message); 284 } 285 286 String styleSheetFile = ""; 287 //Unfortunately, -stylesheetfile is not supported with -doclet? 288 // String styleSheetFileName = ptII + "/doc/doclets/stylesheet.css"; 289 // if (new File(styleSheetFileName).exists()) { 290 // styleSheetFile = "-stylesheetfile " + styleSheetFileName + " "; 291 // } else { 292 // System.out.println("DocBuilder: Warning: could not find " 293 // + styleSheetFileName + ". The JavaDoc output might " 294 // + "be hard to read from within Vergil."); 295 // } 296 297 // commands.add("which javadoc"); 298 commands.add("javadoc -classpath \"" 299 + StringUtilities.getProperty("java.class.path") 300 + "\" -J-Xmx512m -d doc/codeDoc " + styleSheetFile 301 + "-doclet doc.doclets.PtDoclet " 302 + "-subpackages com:diva:jni:org:ptolemy:thales " 303 + "-exclude ptolemy.apps:ptolemy.copernicus:diva.util.java2d.svg"); 304 commands.add("java -Xmx256m " 305 // ptolemy.ptII.batchMode is checked in MessageHandler. 306 // Avoid hanging yes/no questions and just automatically 307 // cancel because when this command is run the user 308 // has no way to hit enter. 309 + "-Dptolemy.ptII.batchMode=true " + "-classpath \"" 310 + StringUtilities.getProperty("java.class.path") 311 + "\" ptolemy.moml.filter.ActorIndex doc/codeDoc/allNamedObjs.txt " 312 + "\"" + ptII 313 + "/ptolemy/configs/doc/models.txt\" doc/codeDoc"); 314 } 315 } else { 316 commands.add("make codeDoc/js/index.html"); 317 commands.add("make codeDoc" + applicationName 318 + "/doc/codeDoc/tree.html"); 319 commands.add("make APPLICATION=" + applicationName 320 + " \"PTDOCFLAGS=-d doc/codeDoc" + applicationName 321 + "/doc/codeDoc" + " codeDoc" + applicationName 322 + "/ptolemy/actor/lib/Ramp.xml"); 323 commands.add("make APPLICATION=" + applicationName + " codeDoc" 324 + applicationName + "/ptolemy/actor/lib/RampIdx.xml"); 325 } 326 if (commands.size() == 0) { 327 return -1; 328 } 329 } 330 331 _executeCommands.setCommands(commands); 332 333 try { 334 // FIXME: need to put this output in to the UI, if any. 335 _executeCommands.start(); 336 } catch (Exception ex) { 337 StringBuffer errorMessage = new StringBuffer(); 338 Iterator allCommands = commands.iterator(); 339 while (allCommands.hasNext()) { 340 errorMessage.append((String) allCommands.next() + "\n"); 341 } 342 throw new IllegalActionException( 343 "Problem executing the " + "commands:\n" + errorMessage); 344 } 345 return _executeCommands.getLastSubprocessReturnCode(); 346 } 347 348 /////////////////////////////////////////////////////////////////// 349 //// private variables //// 350 351 /** The configuration in which we look up the 352 * _applicationName and _docApplicationSpecializer parameters. 353 */ 354 private Configuration _configuration; 355 356 /** The object that actually executes the commands. 357 */ 358 private ExecuteCommands _executeCommands; 359}