You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AjBuildManager.java 40KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.ajdt.internal.core.builder;
  13. import java.io.BufferedOutputStream;
  14. import java.io.File;
  15. import java.io.FileFilter;
  16. import java.io.FileInputStream;
  17. import java.io.IOException;
  18. import java.io.OutputStream;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.HashMap;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Locale;
  25. import java.util.Map;
  26. import java.util.jar.Attributes;
  27. import java.util.jar.JarFile;
  28. import java.util.jar.JarInputStream;
  29. import java.util.jar.JarOutputStream;
  30. import java.util.jar.Manifest;
  31. import java.util.zip.ZipEntry;
  32. import org.aspectj.ajdt.internal.compiler.AjCompilerAdapter;
  33. import org.aspectj.ajdt.internal.compiler.IBinarySourceProvider;
  34. import org.aspectj.ajdt.internal.compiler.ICompilerAdapter;
  35. import org.aspectj.ajdt.internal.compiler.ICompilerAdapterFactory;
  36. import org.aspectj.ajdt.internal.compiler.IIntermediateResultsRequestor;
  37. import org.aspectj.ajdt.internal.compiler.IOutputClassFileNameProvider;
  38. import org.aspectj.ajdt.internal.compiler.InterimCompilationResult;
  39. import org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment;
  40. import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
  41. import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter;
  42. import org.aspectj.asm.AsmManager;
  43. import org.aspectj.asm.IHierarchy;
  44. import org.aspectj.asm.IProgramElement;
  45. import org.aspectj.asm.internal.ProgramElement;
  46. import org.aspectj.bridge.AbortException;
  47. import org.aspectj.bridge.CountingMessageHandler;
  48. import org.aspectj.bridge.IMessage;
  49. import org.aspectj.bridge.IMessageHandler;
  50. import org.aspectj.bridge.IProgressListener;
  51. import org.aspectj.bridge.Message;
  52. import org.aspectj.bridge.MessageUtil;
  53. import org.aspectj.bridge.SourceLocation;
  54. import org.aspectj.bridge.Version;
  55. import org.aspectj.bridge.context.CompilationAndWeavingContext;
  56. import org.aspectj.bridge.context.ContextFormatter;
  57. import org.aspectj.bridge.context.ContextToken;
  58. import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
  59. import org.aspectj.org.eclipse.jdt.core.compiler.IProblem;
  60. import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
  61. import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
  62. import org.aspectj.org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
  63. import org.aspectj.org.eclipse.jdt.internal.compiler.ICompilerRequestor;
  64. import org.aspectj.org.eclipse.jdt.internal.compiler.IProblemFactory;
  65. import org.aspectj.org.eclipse.jdt.internal.compiler.batch.ClasspathDirectory;
  66. import org.aspectj.org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
  67. import org.aspectj.org.eclipse.jdt.internal.compiler.batch.FileSystem;
  68. import org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
  69. import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
  70. import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
  71. import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
  72. import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
  73. import org.aspectj.org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
  74. import org.aspectj.util.FileUtil;
  75. import org.aspectj.weaver.Dump;
  76. import org.aspectj.weaver.ResolvedType;
  77. import org.aspectj.weaver.World;
  78. import org.aspectj.weaver.bcel.BcelWeaver;
  79. import org.aspectj.weaver.bcel.BcelWorld;
  80. import org.aspectj.weaver.bcel.UnwovenClassFile;
  81. import org.eclipse.core.runtime.OperationCanceledException;
  82. //import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
  83. public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourceProvider,ICompilerAdapterFactory {
  84. private static final String CANT_WRITE_RESULT = "unable to write compilation result";
  85. private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
  86. static final boolean COPY_INPATH_DIR_RESOURCES = false;
  87. static final boolean FAIL_IF_RUNTIME_NOT_FOUND = false;
  88. private static final FileFilter binarySourceFilter =
  89. new FileFilter() {
  90. public boolean accept(File f) {
  91. return f.getName().endsWith(".class");
  92. }};
  93. /**
  94. * This builder is static so that it can be subclassed and reset. However, note
  95. * that there is only one builder present, so if two extendsion reset it, only
  96. * the latter will get used.
  97. */
  98. private static AsmHierarchyBuilder asmHierarchyBuilder = new AsmHierarchyBuilder();
  99. static {
  100. CompilationAndWeavingContext.registerFormatter(
  101. CompilationAndWeavingContext.BATCH_BUILD, new AjBuildContexFormatter());
  102. CompilationAndWeavingContext.registerFormatter(
  103. CompilationAndWeavingContext.INCREMENTAL_BUILD, new AjBuildContexFormatter());
  104. }
  105. private IProgressListener progressListener = null;
  106. private int compiledCount;
  107. private int sourceFileCount;
  108. private JarOutputStream zos;
  109. private boolean batchCompile = true;
  110. private INameEnvironment environment;
  111. private Map /* String -> List<UCF>*/ binarySourcesForTheNextCompile = new HashMap();
  112. // FIXME asc should this really be in here?
  113. private IHierarchy structureModel;
  114. public AjBuildConfig buildConfig;
  115. AjState state = new AjState(this);
  116. public BcelWeaver getWeaver() { return state.getWeaver();}
  117. public BcelWorld getBcelWorld() { return state.getBcelWorld();}
  118. public CountingMessageHandler handler;
  119. public AjBuildManager(IMessageHandler holder) {
  120. super();
  121. this.handler = CountingMessageHandler.makeCountingMessageHandler(holder);
  122. }
  123. /** @return true if we should generate a model as a side-effect */
  124. public boolean doGenerateModel() {
  125. return buildConfig.isGenerateModelMode();
  126. }
  127. public boolean batchBuild(
  128. AjBuildConfig buildConfig,
  129. IMessageHandler baseHandler)
  130. throws IOException, AbortException {
  131. return doBuild(buildConfig, baseHandler, true);
  132. }
  133. public boolean incrementalBuild(
  134. AjBuildConfig buildConfig,
  135. IMessageHandler baseHandler)
  136. throws IOException, AbortException {
  137. return doBuild(buildConfig, baseHandler, false);
  138. }
  139. /** @throws AbortException if check for runtime fails */
  140. protected boolean doBuild(
  141. AjBuildConfig buildConfig,
  142. IMessageHandler baseHandler,
  143. boolean batch) throws IOException, AbortException {
  144. boolean ret = true;
  145. batchCompile = batch;
  146. int phase = batch ? CompilationAndWeavingContext.BATCH_BUILD : CompilationAndWeavingContext.INCREMENTAL_BUILD;
  147. ContextToken ct = CompilationAndWeavingContext.enteringPhase(phase ,buildConfig);
  148. try {
  149. if (batch) {
  150. this.state = new AjState(this);
  151. }
  152. boolean canIncremental = state.prepareForNextBuild(buildConfig);
  153. if (!canIncremental && !batch) { // retry as batch?
  154. return doBuild(buildConfig, baseHandler, true);
  155. }
  156. this.handler =
  157. CountingMessageHandler.makeCountingMessageHandler(baseHandler);
  158. // XXX duplicate, no? remove?
  159. String check = checkRtJar(buildConfig);
  160. if (check != null) {
  161. if (FAIL_IF_RUNTIME_NOT_FOUND) {
  162. MessageUtil.error(handler, check);
  163. return false;
  164. } else {
  165. MessageUtil.warn(handler, check);
  166. }
  167. }
  168. // if (batch) {
  169. setBuildConfig(buildConfig);
  170. //}
  171. if (batch || !AsmManager.attemptIncrementalModelRepairs) {
  172. // if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
  173. setupModel(buildConfig);
  174. // }
  175. }
  176. if (batch) {
  177. initBcelWorld(handler);
  178. }
  179. if (handler.hasErrors()) {
  180. return false;
  181. }
  182. if (buildConfig.getOutputJar() != null) {
  183. if (!openOutputStream(buildConfig.getOutputJar())) return false;
  184. }
  185. if (batch) {
  186. // System.err.println("XXXX batch: " + buildConfig.getFiles());
  187. if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
  188. getWorld().setModel(AsmManager.getDefault().getHierarchy());
  189. // in incremental build, only get updated model?
  190. }
  191. binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(true);
  192. performCompilation(buildConfig.getFiles());
  193. if (handler.hasErrors()) {
  194. return false;
  195. }
  196. if (AsmManager.isReporting())
  197. AsmManager.getDefault().reportModelInfo("After a batch build");
  198. } else {
  199. // done already?
  200. // if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
  201. // bcelWorld.setModel(StructureModelManager.INSTANCE.getStructureModel());
  202. // }
  203. // System.err.println("XXXX start inc ");
  204. binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(true);
  205. List files = state.getFilesToCompile(true);
  206. if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode())
  207. if (AsmManager.attemptIncrementalModelRepairs)
  208. AsmManager.getDefault().processDelta(files,state.addedFiles,state.deletedFiles);
  209. boolean hereWeGoAgain = !(files.isEmpty() && binarySourcesForTheNextCompile.isEmpty());
  210. for (int i = 0; (i < 5) && hereWeGoAgain; i++) {
  211. // System.err.println("XXXX inc: " + files);
  212. performCompilation(files);
  213. if (handler.hasErrors() || (progressListener!=null && progressListener.isCancelledRequested())) {
  214. return false;
  215. }
  216. binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(false);
  217. files = state.getFilesToCompile(false);
  218. hereWeGoAgain = !(files.isEmpty() && binarySourcesForTheNextCompile.isEmpty());
  219. // TODO Andy - Needs some thought here...
  220. // I think here we might want to pass empty addedFiles/deletedFiles as they were
  221. // dealt with on the first call to processDelta - we are going through this loop
  222. // again because in compiling something we found something else we needed to
  223. // rebuild. But what case causes this?
  224. if (hereWeGoAgain)
  225. if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode())
  226. if (AsmManager.attemptIncrementalModelRepairs)
  227. AsmManager.getDefault().processDelta(files,state.addedFiles,state.deletedFiles);
  228. }
  229. if (!files.isEmpty()) {
  230. return batchBuild(buildConfig, baseHandler);
  231. } else {
  232. if (AsmManager.isReporting())
  233. AsmManager.getDefault().reportModelInfo("After an incremental build");
  234. }
  235. }
  236. // XXX not in Mik's incremental
  237. if (buildConfig.isEmacsSymMode()) {
  238. new org.aspectj.ajdt.internal.core.builder.EmacsStructureModelManager().externalizeModel();
  239. }
  240. // have to tell state we succeeded or next is not incremental
  241. state.successfulCompile(buildConfig,batch);
  242. copyResourcesToDestination();
  243. /*boolean weaved = *///weaveAndGenerateClassFiles();
  244. // if not weaved, then no-op build, no model changes
  245. // but always returns true
  246. // XXX weaved not in Mik's incremental
  247. if (buildConfig.isGenerateModelMode()) {
  248. AsmManager.getDefault().fireModelUpdated();
  249. }
  250. } finally {
  251. CompilationAndWeavingContext.leavingPhase(ct);
  252. if (zos != null) {
  253. closeOutputStream(buildConfig.getOutputJar());
  254. }
  255. ret = !handler.hasErrors();
  256. getBcelWorld().tidyUp();
  257. // bug 59895, don't release reference to handler as may be needed by a nested call
  258. //handler = null;
  259. }
  260. return ret;
  261. }
  262. private boolean openOutputStream(File outJar) {
  263. try {
  264. OutputStream os = FileUtil.makeOutputStream(buildConfig.getOutputJar());
  265. zos = new JarOutputStream(os,getWeaver().getManifest(true));
  266. } catch (IOException ex) {
  267. IMessage message =
  268. new Message("Unable to open outjar "
  269. + outJar.getPath()
  270. + "(" + ex.getMessage()
  271. + ")",
  272. new SourceLocation(outJar,0),
  273. true);
  274. handler.handleMessage(message);
  275. return false;
  276. }
  277. return true;
  278. }
  279. private void closeOutputStream(File outJar) {
  280. try {
  281. if (zos != null) zos.close();
  282. zos = null;
  283. /* Ensure we don't write an incomplete JAR bug-71339 */
  284. if (handler.hasErrors()) {
  285. outJar.delete();
  286. }
  287. } catch (IOException ex) {
  288. IMessage message =
  289. new Message("Unable to write outjar "
  290. + outJar.getPath()
  291. + "(" + ex.getMessage()
  292. + ")",
  293. new SourceLocation(outJar,0),
  294. true);
  295. handler.handleMessage(message);
  296. }
  297. }
  298. private void copyResourcesToDestination() throws IOException {
  299. // resources that we need to copy are contained in the injars and inpath only
  300. for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
  301. File inJar = (File)i.next();
  302. copyResourcesFromJarFile(inJar);
  303. }
  304. for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext(); ) {
  305. File inPathElement = (File)i.next();
  306. if (inPathElement.isDirectory()) {
  307. copyResourcesFromDirectory(inPathElement);
  308. } else {
  309. copyResourcesFromJarFile(inPathElement);
  310. }
  311. }
  312. if (buildConfig.getSourcePathResources() != null) {
  313. for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext(); ) {
  314. String resource = (String)i.next();
  315. File from = (File)buildConfig.getSourcePathResources().get(resource);
  316. copyResourcesFromFile(from,resource,from);
  317. }
  318. }
  319. writeManifest();
  320. }
  321. private void copyResourcesFromJarFile(File jarFile) throws IOException {
  322. JarInputStream inStream = null;
  323. try {
  324. inStream = new JarInputStream(new FileInputStream(jarFile));
  325. while (true) {
  326. ZipEntry entry = inStream.getNextEntry();
  327. if (entry == null) break;
  328. String filename = entry.getName();
  329. // System.out.println("? copyResourcesFromJarFile() filename='" + filename +"'");
  330. if (!entry.isDirectory() && acceptResource(filename)) {
  331. byte[] bytes = FileUtil.readAsByteArray(inStream);
  332. writeResource(filename,bytes,jarFile);
  333. }
  334. inStream.closeEntry();
  335. }
  336. } finally {
  337. if (inStream != null) inStream.close();
  338. }
  339. }
  340. private void copyResourcesFromDirectory(File dir) throws IOException {
  341. if (!COPY_INPATH_DIR_RESOURCES) return;
  342. // Get a list of all files (i.e. everything that isnt a directory)
  343. File[] files = FileUtil.listFiles(dir,new FileFilter() {
  344. public boolean accept(File f) {
  345. boolean accept = !(f.isDirectory() || f.getName().endsWith(".class")) ;
  346. return accept;
  347. }
  348. });
  349. // For each file, add it either as a real .class file or as a resource
  350. for (int i = 0; i < files.length; i++) {
  351. // ASSERT: files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath()
  352. // or we are in trouble...
  353. String filename = files[i].getAbsolutePath().substring(
  354. dir.getAbsolutePath().length()+1);
  355. copyResourcesFromFile(files[i],filename,dir);
  356. }
  357. }
  358. private void copyResourcesFromFile(File f,String filename,File src) throws IOException {
  359. if (!acceptResource(filename)) return;
  360. FileInputStream fis = null;
  361. try {
  362. fis = new FileInputStream(f);
  363. byte[] bytes = FileUtil.readAsByteArray(fis);
  364. // String relativePath = files[i].getPath();
  365. writeResource(filename,bytes,src);
  366. } finally {
  367. if (fis != null) fis.close();
  368. }
  369. }
  370. private void writeResource(String filename, byte[] content, File srcLocation) throws IOException {
  371. if (state.resources.contains(filename)) {
  372. IMessage msg = new Message("duplicate resource: '" + filename + "'",
  373. IMessage.WARNING,
  374. null,
  375. new SourceLocation(srcLocation,0));
  376. handler.handleMessage(msg);
  377. return;
  378. }
  379. if (zos != null) {
  380. ZipEntry newEntry = new ZipEntry(filename); //??? get compression scheme right
  381. zos.putNextEntry(newEntry);
  382. zos.write(content);
  383. zos.closeEntry();
  384. } else {
  385. OutputStream fos =
  386. FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),filename));
  387. fos.write(content);
  388. fos.close();
  389. }
  390. state.resources.add(filename);
  391. }
  392. /*
  393. * If we are writing to an output directory copy the manifest but only
  394. * if we already have one
  395. */
  396. private void writeManifest () throws IOException {
  397. Manifest manifest = getWeaver().getManifest(false);
  398. if (manifest != null && zos == null) {
  399. OutputStream fos =
  400. FileUtil.makeOutputStream(new File(buildConfig.getOutputDir(),MANIFEST_NAME));
  401. manifest.write(fos);
  402. fos.close();
  403. }
  404. }
  405. private boolean acceptResource(String resourceName) {
  406. if (
  407. (resourceName.startsWith("CVS/")) ||
  408. (resourceName.indexOf("/CVS/") != -1) ||
  409. (resourceName.endsWith("/CVS")) ||
  410. (resourceName.endsWith(".class")) ||
  411. (resourceName.toUpperCase().equals(MANIFEST_NAME))
  412. )
  413. {
  414. return false;
  415. } else {
  416. return true;
  417. }
  418. }
  419. // public static void dumprels() {
  420. // IRelationshipMap irm = AsmManager.getDefault().getRelationshipMap();
  421. // int ctr = 1;
  422. // Set entries = irm.getEntries();
  423. // for (Iterator iter = entries.iterator(); iter.hasNext();) {
  424. // String hid = (String) iter.next();
  425. // List rels = irm.get(hid);
  426. // for (Iterator iterator = rels.iterator(); iterator.hasNext();) {
  427. // IRelationship ir = (IRelationship) iterator.next();
  428. // List targets = ir.getTargets();
  429. // for (Iterator iterator2 = targets.iterator();
  430. // iterator2.hasNext();
  431. // ) {
  432. // String thid = (String) iterator2.next();
  433. // System.err.println("Hid:"+(ctr++)+":(targets="+targets.size()+") "+hid+" ("+ir.getName()+") "+thid);
  434. // }
  435. // }
  436. // }
  437. // }
  438. /**
  439. * Responsible for managing the ASM model between builds. Contains the policy for
  440. * maintaining the persistance of elements in the model.
  441. *
  442. * This code is driven before each 'fresh' (batch) build to create
  443. * a new model.
  444. */
  445. private void setupModel(AjBuildConfig config) {
  446. AsmManager.setCreatingModel(config.isEmacsSymMode() || config.isGenerateModelMode());
  447. if (!AsmManager.isCreatingModel()) return;
  448. AsmManager.getDefault().createNewASM();
  449. // AsmManager.getDefault().getRelationshipMap().clear();
  450. IHierarchy model = AsmManager.getDefault().getHierarchy();
  451. String rootLabel = "<root>";
  452. IProgramElement.Kind kind = IProgramElement.Kind.FILE_JAVA;
  453. if (buildConfig.getConfigFile() != null) {
  454. rootLabel = buildConfig.getConfigFile().getName();
  455. model.setConfigFile(buildConfig.getConfigFile().getAbsolutePath());
  456. kind = IProgramElement.Kind.FILE_LST;
  457. }
  458. model.setRoot(new ProgramElement(rootLabel, kind, new ArrayList()));
  459. model.setFileMap(new HashMap());
  460. setStructureModel(model);
  461. state.setStructureModel(model);
  462. state.setRelationshipMap(AsmManager.getDefault().getRelationshipMap());
  463. }
  464. //
  465. // private void dumplist(List l) {
  466. // System.err.println("---- "+l.size());
  467. // for (int i =0 ;i<l.size();i++) System.err.println(i+"\t "+l.get(i));
  468. // }
  469. // private void accumulateFileNodes(IProgramElement ipe,List store) {
  470. // if (ipe.getKind()==IProgramElement.Kind.FILE_JAVA ||
  471. // ipe.getKind()==IProgramElement.Kind.FILE_ASPECTJ) {
  472. // if (!ipe.getName().equals("<root>")) {
  473. // store.add(ipe);
  474. // return;
  475. // }
  476. // }
  477. // for (Iterator i = ipe.getChildren().iterator();i.hasNext();) {
  478. // accumulateFileNodes((IProgramElement)i.next(),store);
  479. // }
  480. // }
  481. /** init only on initial batch compile? no file-specific options */
  482. private void initBcelWorld(IMessageHandler handler) throws IOException {
  483. List cp = buildConfig.getBootclasspath();
  484. cp.addAll(buildConfig.getClasspath());
  485. BcelWorld bcelWorld = new BcelWorld(cp, handler, null);
  486. bcelWorld.setBehaveInJava5Way(buildConfig.getBehaveInJava5Way());
  487. bcelWorld.setXnoInline(buildConfig.isXnoInline());
  488. bcelWorld.setXlazyTjp(buildConfig.isXlazyTjp());
  489. bcelWorld.setXHasMemberSupportEnabled(buildConfig.isXHasMemberEnabled());
  490. bcelWorld.setPinpointMode(buildConfig.isXdevPinpoint());
  491. BcelWeaver bcelWeaver = new BcelWeaver(bcelWorld);
  492. state.setWorld(bcelWorld);
  493. state.setWeaver(bcelWeaver);
  494. state.binarySourceFiles = new HashMap();
  495. for (Iterator i = buildConfig.getAspectpath().iterator(); i.hasNext();) {
  496. File f = (File) i.next();
  497. bcelWeaver.addLibraryJarFile(f);
  498. }
  499. // String lintMode = buildConfig.getLintMode();
  500. if (buildConfig.getLintMode().equals(AjBuildConfig.AJLINT_DEFAULT)) {
  501. bcelWorld.getLint().loadDefaultProperties();
  502. } else {
  503. bcelWorld.getLint().setAll(buildConfig.getLintMode());
  504. }
  505. if (buildConfig.getLintSpecFile() != null) {
  506. bcelWorld.getLint().setFromProperties(buildConfig.getLintSpecFile());
  507. }
  508. //??? incremental issues
  509. for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
  510. File inJar = (File)i.next();
  511. List unwovenClasses = bcelWeaver.addJarFile(inJar, buildConfig.getOutputDir(),false);
  512. state.binarySourceFiles.put(inJar.getPath(), unwovenClasses);
  513. }
  514. for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext(); ) {
  515. File inPathElement = (File)i.next();
  516. if (!inPathElement.isDirectory()) {
  517. // its a jar file on the inpath
  518. // the weaver method can actually handle dirs, but we don't call it, see next block
  519. List unwovenClasses = bcelWeaver.addJarFile(inPathElement,buildConfig.getOutputDir(),true);
  520. state.binarySourceFiles.put(inPathElement.getPath(),unwovenClasses);
  521. } else {
  522. // add each class file in an in-dir individually, this gives us the best error reporting
  523. // (they are like 'source' files then), and enables a cleaner incremental treatment of
  524. // class file changes in indirs.
  525. File[] binSrcs = FileUtil.listFiles(inPathElement, binarySourceFilter);
  526. for (int j = 0; j < binSrcs.length; j++) {
  527. UnwovenClassFile ucf =
  528. bcelWeaver.addClassFile(binSrcs[j], inPathElement, buildConfig.getOutputDir());
  529. List ucfl = new ArrayList();
  530. ucfl.add(ucf);
  531. state.binarySourceFiles.put(binSrcs[j].getPath(),ucfl);
  532. }
  533. }
  534. }
  535. bcelWeaver.setReweavableMode(buildConfig.isXreweavable(),buildConfig.getXreweavableCompressClasses());
  536. //check for org.aspectj.runtime.JoinPoint
  537. ResolvedType joinPoint = bcelWorld.resolve("org.aspectj.lang.JoinPoint");
  538. if (joinPoint.isMissing()) {
  539. IMessage message =
  540. new Message("classpath error: unable to find org.aspectj.lang.JoinPoint (check that aspectjrt.jar is in your classpath)",
  541. null,
  542. true);
  543. handler.handleMessage(message);
  544. }
  545. }
  546. public World getWorld() {
  547. return getBcelWorld();
  548. }
  549. void addAspectClassFilesToWeaver(List addedClassFiles) throws IOException {
  550. for (Iterator i = addedClassFiles.iterator(); i.hasNext(); ) {
  551. UnwovenClassFile classFile = (UnwovenClassFile) i.next();
  552. getWeaver().addClassFile(classFile);
  553. }
  554. }
  555. // public boolean weaveAndGenerateClassFiles() throws IOException {
  556. // handler.handleMessage(MessageUtil.info("weaving"));
  557. // if (progressListener != null) progressListener.setText("weaving aspects");
  558. // bcelWeaver.setProgressListener(progressListener, 0.5, 0.5/state.addedClassFiles.size());
  559. // //!!! doesn't provide intermediate progress during weaving
  560. // // XXX add all aspects even during incremental builds?
  561. // addAspectClassFilesToWeaver(state.addedClassFiles);
  562. // if (buildConfig.isNoWeave()) {
  563. // if (buildConfig.getOutputJar() != null) {
  564. // bcelWeaver.dumpUnwoven(buildConfig.getOutputJar());
  565. // } else {
  566. // bcelWeaver.dumpUnwoven();
  567. // bcelWeaver.dumpResourcesToOutPath();
  568. // }
  569. // } else {
  570. // if (buildConfig.getOutputJar() != null) {
  571. // bcelWeaver.weave(buildConfig.getOutputJar());
  572. // } else {
  573. // bcelWeaver.weave();
  574. // bcelWeaver.dumpResourcesToOutPath();
  575. // }
  576. // }
  577. // if (progressListener != null) progressListener.setProgress(1.0);
  578. // return true;
  579. // //return messageAdapter.getErrorCount() == 0; //!javaBuilder.notifier.anyErrors();
  580. // }
  581. public FileSystem getLibraryAccess(String[] classpaths, String[] filenames) {
  582. String defaultEncoding = buildConfig.getOptions().defaultEncoding;
  583. if ("".equals(defaultEncoding)) //$NON-NLS-1$
  584. defaultEncoding = null; //$NON-NLS-1$
  585. // Bug 46671: We need an array as long as the number of elements in the classpath - *even though* not every
  586. // element of the classpath is likely to be a directory. If we ensure every element of the array is set to
  587. // only look for BINARY, then we make sure that for any classpath element that is a directory, we won't build
  588. // a classpathDirectory object that will attempt to look for source when it can't find binary.
  589. int[] classpathModes = new int[classpaths.length];
  590. for (int i =0 ;i<classpaths.length;i++) classpathModes[i]=ClasspathDirectory.BINARY;
  591. return new FileSystem(classpaths, filenames, defaultEncoding,classpathModes);
  592. }
  593. public IProblemFactory getProblemFactory() {
  594. return new DefaultProblemFactory(Locale.getDefault());
  595. }
  596. /*
  597. * Build the set of compilation source units
  598. */
  599. public CompilationUnit[] getCompilationUnits(String[] filenames, String[] encodings) {
  600. int fileCount = filenames.length;
  601. CompilationUnit[] units = new CompilationUnit[fileCount];
  602. // HashtableOfObject knownFileNames = new HashtableOfObject(fileCount);
  603. String defaultEncoding = buildConfig.getOptions().defaultEncoding;
  604. if ("".equals(defaultEncoding)) //$NON-NLS-1$
  605. defaultEncoding = null; //$NON-NLS-1$
  606. for (int i = 0; i < fileCount; i++) {
  607. String encoding = encodings[i];
  608. if (encoding == null)
  609. encoding = defaultEncoding;
  610. units[i] = new CompilationUnit(null, filenames[i], encoding);
  611. }
  612. return units;
  613. }
  614. public String extractDestinationPathFromSourceFile(CompilationResult result) {
  615. ICompilationUnit compilationUnit = result.compilationUnit;
  616. if (compilationUnit != null) {
  617. char[] fileName = compilationUnit.getFileName();
  618. int lastIndex = CharOperation.lastIndexOf(java.io.File.separatorChar, fileName);
  619. if (lastIndex == -1) {
  620. return System.getProperty("user.dir"); //$NON-NLS-1$
  621. }
  622. return new String(CharOperation.subarray(fileName, 0, lastIndex));
  623. }
  624. return System.getProperty("user.dir"); //$NON-NLS-1$
  625. }
  626. public void performCompilation(List files) {
  627. if (progressListener != null) {
  628. compiledCount=0;
  629. sourceFileCount = files.size();
  630. progressListener.setText("compiling source files");
  631. }
  632. //System.err.println("got files: " + files);
  633. String[] filenames = new String[files.size()];
  634. String[] encodings = new String[files.size()];
  635. //System.err.println("filename: " + this.filenames);
  636. for (int i=0; i < files.size(); i++) {
  637. filenames[i] = ((File)files.get(i)).getPath();
  638. }
  639. List cps = buildConfig.getFullClasspath();
  640. Dump.saveFullClasspath(cps);
  641. String[] classpaths = new String[cps.size()];
  642. for (int i=0; i < cps.size(); i++) {
  643. classpaths[i] = (String)cps.get(i);
  644. }
  645. //System.out.println("compiling");
  646. environment = getLibraryAccess(classpaths, filenames);
  647. if (!state.classesFromName.isEmpty()) {
  648. environment = new StatefulNameEnvironment(environment, state.classesFromName);
  649. }
  650. org.aspectj.ajdt.internal.compiler.CompilerAdapter.setCompilerAdapterFactory(this);
  651. org.aspectj.org.eclipse.jdt.internal.compiler.Compiler compiler =
  652. new org.aspectj.org.eclipse.jdt.internal.compiler.Compiler(environment,
  653. DefaultErrorHandlingPolicies.proceedWithAllProblems(),
  654. buildConfig.getOptions().getMap(),
  655. getBatchRequestor(),
  656. getProblemFactory());
  657. CompilerOptions options = compiler.options;
  658. options.produceReferenceInfo = true; //TODO turn off when not needed
  659. try {
  660. compiler.compile(getCompilationUnits(filenames, encodings));
  661. } catch (OperationCanceledException oce) {
  662. handler.handleMessage(new Message("build cancelled:"+oce.getMessage(),IMessage.WARNING,null,null));
  663. }
  664. // cleanup
  665. environment.cleanup();
  666. environment = null;
  667. }
  668. /*
  669. * Answer the component to which will be handed back compilation results from the compiler
  670. */
  671. public IIntermediateResultsRequestor getInterimResultRequestor() {
  672. return new IIntermediateResultsRequestor() {
  673. public void acceptResult(InterimCompilationResult result) {
  674. if (progressListener != null) {
  675. compiledCount++;
  676. progressListener.setProgress((compiledCount/2.0)/sourceFileCount);
  677. progressListener.setText("compiled: " + result.fileName());
  678. }
  679. state.noteResult(result);
  680. if (progressListener!=null && progressListener.isCancelledRequested()) {
  681. throw new AbortCompilation(true,
  682. new OperationCanceledException("Compilation cancelled as requested"));
  683. }
  684. }
  685. };
  686. }
  687. public ICompilerRequestor getBatchRequestor() {
  688. return new ICompilerRequestor() {
  689. public void acceptResult(CompilationResult unitResult) {
  690. // end of compile, must now write the results to the output destination
  691. // this is either a jar file or a file in a directory
  692. if (!(unitResult.hasErrors() && !proceedOnError())) {
  693. Collection classFiles = unitResult.compiledTypes.values();
  694. for (Iterator iter = classFiles.iterator(); iter.hasNext();) {
  695. ClassFile classFile = (ClassFile) iter.next();
  696. String filename = new String(classFile.fileName());
  697. filename = filename.replace('/', File.separatorChar) + ".class";
  698. try {
  699. if (buildConfig.getOutputJar() == null) {
  700. writeDirectoryEntry(unitResult, classFile,filename);
  701. } else {
  702. writeZipEntry(classFile,filename);
  703. }
  704. } catch (IOException ex) {
  705. IMessage message = EclipseAdapterUtils.makeErrorMessage(
  706. new String(unitResult.fileName),
  707. CANT_WRITE_RESULT,
  708. ex);
  709. handler.handleMessage(message);
  710. }
  711. }
  712. }
  713. if (unitResult.hasProblems() || unitResult.hasTasks()) {
  714. IProblem[] problems = unitResult.getAllProblems();
  715. for (int i=0; i < problems.length; i++) {
  716. IMessage message =
  717. EclipseAdapterUtils.makeMessage(unitResult.compilationUnit, problems[i]);
  718. handler.handleMessage(message);
  719. }
  720. }
  721. }
  722. private void writeDirectoryEntry(
  723. CompilationResult unitResult,
  724. ClassFile classFile,
  725. String filename)
  726. throws IOException {
  727. File destinationPath = buildConfig.getOutputDir();
  728. String outFile;
  729. if (destinationPath == null) {
  730. outFile = new File(filename).getName();
  731. outFile = new File(extractDestinationPathFromSourceFile(unitResult), outFile).getPath();
  732. } else {
  733. outFile = new File(destinationPath, filename).getPath();
  734. }
  735. BufferedOutputStream os =
  736. FileUtil.makeOutputStream(new File(outFile));
  737. os.write(classFile.getBytes());
  738. os.close();
  739. }
  740. private void writeZipEntry(ClassFile classFile, String name)
  741. throws IOException {
  742. name = name.replace(File.separatorChar,'/');
  743. ZipEntry newEntry = new ZipEntry(name); //??? get compression scheme right
  744. zos.putNextEntry(newEntry);
  745. zos.write(classFile.getBytes());
  746. zos.closeEntry();
  747. }
  748. };
  749. }
  750. protected boolean proceedOnError() {
  751. return buildConfig.getProceedOnError();
  752. }
  753. // public void noteClassFiles(AjCompiler.InterimResult result) {
  754. // if (result == null) return;
  755. // CompilationResult unitResult = result.result;
  756. // String sourceFileName = result.fileName();
  757. // if (!(unitResult.hasErrors() && !proceedOnError())) {
  758. // List unwovenClassFiles = new ArrayList();
  759. // Enumeration classFiles = unitResult.compiledTypes.elements();
  760. // while (classFiles.hasMoreElements()) {
  761. // ClassFile classFile = (ClassFile) classFiles.nextElement();
  762. // String filename = new String(classFile.fileName());
  763. // filename = filename.replace('/', File.separatorChar) + ".class";
  764. //
  765. // File destinationPath = buildConfig.getOutputDir();
  766. // if (destinationPath == null) {
  767. // filename = new File(filename).getName();
  768. // filename = new File(extractDestinationPathFromSourceFile(unitResult), filename).getPath();
  769. // } else {
  770. // filename = new File(destinationPath, filename).getPath();
  771. // }
  772. //
  773. // //System.out.println("classfile: " + filename);
  774. // unwovenClassFiles.add(new UnwovenClassFile(filename, classFile.getBytes()));
  775. // }
  776. // state.noteClassesFromFile(unitResult, sourceFileName, unwovenClassFiles);
  777. //// System.out.println("file: " + sourceFileName);
  778. //// for (int i=0; i < unitResult.simpleNameReferences.length; i++) {
  779. //// System.out.println("simple: " + new String(unitResult.simpleNameReferences[i]));
  780. //// }
  781. //// for (int i=0; i < unitResult.qualifiedReferences.length; i++) {
  782. //// System.out.println("qualified: " +
  783. //// new String(CharOperation.concatWith(unitResult.qualifiedReferences[i], '/')));
  784. //// }
  785. // } else {
  786. // state.noteClassesFromFile(null, sourceFileName, Collections.EMPTY_LIST);
  787. // }
  788. // }
  789. //
  790. private void setBuildConfig(AjBuildConfig buildConfig) {
  791. this.buildConfig = buildConfig;
  792. handler.reset();
  793. }
  794. String makeClasspathString(AjBuildConfig buildConfig) {
  795. if (buildConfig == null || buildConfig.getFullClasspath() == null) return "";
  796. StringBuffer buf = new StringBuffer();
  797. boolean first = true;
  798. for (Iterator it = buildConfig.getFullClasspath().iterator(); it.hasNext(); ) {
  799. if (first) { first = false; }
  800. else { buf.append(File.pathSeparator); }
  801. buf.append(it.next().toString());
  802. }
  803. return buf.toString();
  804. }
  805. /**
  806. * This will return null if aspectjrt.jar is present and has the correct version.
  807. * Otherwise it will return a string message indicating the problem.
  808. */
  809. public String checkRtJar(AjBuildConfig buildConfig) {
  810. // omitting dev info
  811. if (Version.text.equals(Version.DEVELOPMENT)) {
  812. // in the development version we can't do this test usefully
  813. // MessageUtil.info(holder, "running development version of aspectj compiler");
  814. return null;
  815. }
  816. if (buildConfig == null || buildConfig.getFullClasspath() == null) return "no classpath specified";
  817. for (Iterator it = buildConfig.getFullClasspath().iterator(); it.hasNext(); ) {
  818. File p = new File( (String)it.next() );
  819. if (p.isFile() && p.getName().equals("aspectjrt.jar")) {
  820. try {
  821. String version = null;
  822. Manifest manifest = new JarFile(p).getManifest();
  823. if (manifest == null) {
  824. return "no manifest found in " + p.getAbsolutePath() +
  825. ", expected " + Version.text;
  826. }
  827. Attributes attr = manifest.getAttributes("org/aspectj/lang/");
  828. if (null != attr) {
  829. version = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
  830. if (null != version) {
  831. version = version.trim();
  832. }
  833. }
  834. // assume that users of development aspectjrt.jar know what they're doing
  835. if (Version.DEVELOPMENT.equals(version)) {
  836. // MessageUtil.info(holder,
  837. // "running with development version of aspectjrt.jar in " +
  838. // p.getAbsolutePath());
  839. return null;
  840. } else if (!Version.text.equals(version)) {
  841. return "bad version number found in " + p.getAbsolutePath() +
  842. " expected " + Version.text + " found " + version;
  843. }
  844. } catch (IOException ioe) {
  845. return "bad jar file found in " + p.getAbsolutePath() + " error: " + ioe;
  846. }
  847. return null;
  848. } else {
  849. // might want to catch other classpath errors
  850. }
  851. }
  852. return "couldn't find aspectjrt.jar on classpath, checked: " + makeClasspathString(buildConfig);
  853. }
  854. public String toString() {
  855. StringBuffer buf = new StringBuffer();
  856. buf.append("AjBuildManager(");
  857. buf.append(")");
  858. return buf.toString();
  859. }
  860. public void setStructureModel(IHierarchy structureModel) {
  861. this.structureModel = structureModel;
  862. }
  863. /**
  864. * Returns null if there is no structure model
  865. */
  866. public IHierarchy getStructureModel() {
  867. return structureModel;
  868. }
  869. public IProgressListener getProgressListener() {
  870. return progressListener;
  871. }
  872. public void setProgressListener(IProgressListener progressListener) {
  873. this.progressListener = progressListener;
  874. }
  875. /* (non-Javadoc)
  876. * @see org.aspectj.ajdt.internal.compiler.AjCompiler.IOutputClassFileNameProvider#getOutputClassFileName(char[])
  877. */
  878. public String getOutputClassFileName(char[] eclipseClassFileName, CompilationResult result) {
  879. String filename = new String(eclipseClassFileName);
  880. filename = filename.replace('/', File.separatorChar) + ".class";
  881. File destinationPath = buildConfig.getOutputDir();
  882. String outFile;
  883. if (destinationPath == null) {
  884. outFile = new File(filename).getName();
  885. outFile = new File(extractDestinationPathFromSourceFile(result), outFile).getPath();
  886. } else {
  887. outFile = new File(destinationPath, filename).getPath();
  888. }
  889. return outFile;
  890. }
  891. /* (non-Javadoc)
  892. * @see org.eclipse.jdt.internal.compiler.ICompilerAdapterFactory#getAdapter(org.eclipse.jdt.internal.compiler.Compiler)
  893. */
  894. public ICompilerAdapter getAdapter(org.aspectj.org.eclipse.jdt.internal.compiler.Compiler forCompiler) {
  895. // complete compiler config and return a suitable adapter...
  896. AjProblemReporter pr =
  897. new AjProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(),
  898. forCompiler.options, getProblemFactory());
  899. forCompiler.problemReporter = pr;
  900. AjLookupEnvironment le =
  901. new AjLookupEnvironment(forCompiler, forCompiler.options, pr, environment);
  902. EclipseFactory factory = new EclipseFactory(le,this);
  903. le.factory = factory;
  904. pr.factory = factory;
  905. forCompiler.lookupEnvironment = le;
  906. forCompiler.parser =
  907. new Parser(
  908. pr,
  909. forCompiler.options.parseLiteralExpressionsAsConstants);
  910. return new AjCompilerAdapter(forCompiler,batchCompile,getBcelWorld(),getWeaver(),
  911. factory,
  912. getInterimResultRequestor(),
  913. progressListener,
  914. this, // IOutputFilenameProvider
  915. this, // IBinarySourceProvider
  916. state.binarySourceFiles,
  917. state.resultsFromFile.values(),
  918. buildConfig.isNoWeave(),
  919. buildConfig.getProceedOnError(),
  920. buildConfig.isNoAtAspectJAnnotationProcessing());
  921. }
  922. /* (non-Javadoc)
  923. * @see org.aspectj.ajdt.internal.compiler.IBinarySourceProvider#getBinarySourcesForThisWeave()
  924. */
  925. public Map getBinarySourcesForThisWeave() {
  926. return binarySourcesForTheNextCompile;
  927. }
  928. public static AsmHierarchyBuilder getAsmHierarchyBuilder() {
  929. return asmHierarchyBuilder;
  930. }
  931. /**
  932. * Override the the default hierarchy builder.
  933. */
  934. public static void setAsmHierarchyBuilder(AsmHierarchyBuilder newBuilder) {
  935. asmHierarchyBuilder = newBuilder;
  936. }
  937. public AjState getState() {
  938. return state;
  939. }
  940. public void setState(AjState buildState) {
  941. state = buildState;
  942. }
  943. private static class AjBuildContexFormatter implements ContextFormatter {
  944. public String formatEntry(int phaseId, Object data) {
  945. StringBuffer sb = new StringBuffer();
  946. if (phaseId == CompilationAndWeavingContext.BATCH_BUILD) {
  947. sb.append("batch building ");
  948. } else {
  949. sb.append("incrementally building ");
  950. }
  951. AjBuildConfig config = (AjBuildConfig) data;
  952. List classpath = config.getClasspath();
  953. sb.append("with classpath: ");
  954. for (Iterator iter = classpath.iterator(); iter.hasNext();) {
  955. sb.append(iter.next().toString());
  956. sb.append(File.pathSeparator);
  957. }
  958. return sb.toString();
  959. }
  960. }
  961. }