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 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  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.*;
  14. import java.util.*;
  15. import java.util.jar.*;
  16. import org.aspectj.ajdt.internal.compiler.AjCompiler;
  17. import org.aspectj.ajdt.internal.compiler.lookup.*;
  18. import org.aspectj.ajdt.internal.compiler.parser.AjParser;
  19. import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter;
  20. import org.aspectj.asm.*;
  21. import org.aspectj.asm.internal.*;
  22. import org.aspectj.asm.internal.ProgramElement;
  23. import org.aspectj.bridge.*;
  24. import org.aspectj.weaver.World;
  25. import org.aspectj.weaver.bcel.*;
  26. import org.eclipse.jdt.core.compiler.*;
  27. import org.eclipse.jdt.internal.compiler.*;
  28. import org.eclipse.jdt.internal.compiler.batch.*;
  29. import org.eclipse.jdt.internal.compiler.batch.FileSystem;
  30. import org.eclipse.jdt.internal.compiler.env.*;
  31. import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
  32. import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
  33. import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
  34. public class AjBuildManager {
  35. static final boolean FAIL_IF_RUNTIME_NOT_FOUND = false;
  36. private IProgressListener progressListener = null;
  37. private int compiledCount;
  38. private int sourceFileCount;
  39. private IHierarchy structureModel;
  40. public AjBuildConfig buildConfig;
  41. AjState state = new AjState(this);
  42. BcelWeaver bcelWeaver;
  43. public BcelWorld bcelWorld;
  44. public CountingMessageHandler handler;
  45. public AjBuildManager(IMessageHandler holder) {
  46. super();
  47. this.handler = CountingMessageHandler.makeCountingMessageHandler(holder);
  48. }
  49. /** @return true if we should generate a model as a side-effect */
  50. public boolean doGenerateModel() {
  51. return buildConfig.isGenerateModelMode();
  52. }
  53. public boolean batchBuild(
  54. AjBuildConfig buildConfig,
  55. IMessageHandler baseHandler)
  56. throws IOException, AbortException {
  57. return doBuild(buildConfig, baseHandler, true);
  58. }
  59. public boolean incrementalBuild(
  60. AjBuildConfig buildConfig,
  61. IMessageHandler baseHandler)
  62. throws IOException, AbortException {
  63. return doBuild(buildConfig, baseHandler, false);
  64. }
  65. /** @throws AbortException if check for runtime fails */
  66. protected boolean doBuild(
  67. AjBuildConfig buildConfig,
  68. IMessageHandler baseHandler,
  69. boolean batch) throws IOException, AbortException {
  70. try {
  71. if (batch) {
  72. this.state = new AjState(this);
  73. }
  74. boolean canIncremental = state.prepareForNextBuild(buildConfig);
  75. if (!canIncremental && !batch) { // retry as batch?
  76. return doBuild(buildConfig, baseHandler, true);
  77. }
  78. this.handler =
  79. CountingMessageHandler.makeCountingMessageHandler(baseHandler);
  80. // XXX duplicate, no? remove?
  81. String check = checkRtJar(buildConfig);
  82. if (check != null) {
  83. if (FAIL_IF_RUNTIME_NOT_FOUND) {
  84. MessageUtil.error(handler, check);
  85. return false;
  86. } else {
  87. MessageUtil.warn(handler, check);
  88. }
  89. }
  90. // if (batch) {
  91. setBuildConfig(buildConfig);
  92. //}
  93. setupModel();
  94. if (batch) {
  95. initBcelWorld(handler);
  96. }
  97. if (handler.hasErrors()) {
  98. return false;
  99. }
  100. if (batch) {
  101. // System.err.println("XXXX batch: " + buildConfig.getFiles());
  102. if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
  103. bcelWorld.setModel(AsmManager.getDefault().getHierarchy());
  104. // in incremental build, only get updated model?
  105. }
  106. performCompilation(buildConfig.getFiles());
  107. if (handler.hasErrors()) {
  108. return false;
  109. }
  110. } else {
  111. // done already?
  112. // if (buildConfig.isEmacsSymMode() || buildConfig.isGenerateModelMode()) {
  113. // bcelWorld.setModel(StructureModelManager.INSTANCE.getStructureModel());
  114. // }
  115. // System.err.println("XXXX start inc ");
  116. List files = state.getFilesToCompile(true);
  117. for (int i = 0; (i < 5) && !files.isEmpty(); i++) {
  118. // System.err.println("XXXX inc: " + files);
  119. performCompilation(files);
  120. if (handler.hasErrors()) {
  121. return false;
  122. }
  123. files = state.getFilesToCompile(false);
  124. }
  125. if (!files.isEmpty()) {
  126. return batchBuild(buildConfig, baseHandler);
  127. }
  128. }
  129. // XXX not in Mik's incremental
  130. if (buildConfig.isEmacsSymMode()) {
  131. new org.aspectj.ajdt.internal.core.builder.EmacsStructureModelManager().externalizeModel();
  132. }
  133. // have to tell state we succeeded or next is not incremental
  134. state.successfulCompile(buildConfig);
  135. boolean weaved = weaveAndGenerateClassFiles();
  136. // if not weaved, then no-op build, no model changes
  137. // but always returns true
  138. // XXX weaved not in Mik's incremental
  139. if (buildConfig.isGenerateModelMode()) {
  140. AsmManager.getDefault().fireModelUpdated();
  141. }
  142. return !handler.hasErrors();
  143. } finally {
  144. handler = null;
  145. }
  146. }
  147. private void setupModel() {
  148. String rootLabel = "<root>";
  149. IHierarchy model = AsmManager.getDefault().getHierarchy();
  150. IProgramElement.Kind kind = IProgramElement.Kind.FILE_JAVA;
  151. if (buildConfig.getConfigFile() != null) {
  152. rootLabel = buildConfig.getConfigFile().getName();
  153. model.setConfigFile(
  154. buildConfig.getConfigFile().getAbsolutePath()
  155. );
  156. kind = IProgramElement.Kind.FILE_LST;
  157. }
  158. model.setRoot(new ProgramElement(rootLabel, kind, new ArrayList()));
  159. model.setFileMap(new HashMap());
  160. setStructureModel(model);
  161. }
  162. /** init only on initial batch compile? no file-specific options */
  163. private void initBcelWorld(IMessageHandler handler) throws IOException {
  164. bcelWorld = new BcelWorld(buildConfig.getClasspath(), handler);
  165. bcelWorld.setXnoInline(buildConfig.isXnoInline());
  166. bcelWeaver = new BcelWeaver(bcelWorld);
  167. for (Iterator i = buildConfig.getAspectpath().iterator(); i.hasNext();) {
  168. File f = (File) i.next();
  169. bcelWeaver.addLibraryJarFile(f);
  170. }
  171. String lintMode = buildConfig.getLintMode();
  172. if (buildConfig.getLintMode().equals(AjBuildConfig.AJLINT_DEFAULT)) {
  173. bcelWorld.getLint().loadDefaultProperties();
  174. } else {
  175. bcelWorld.getLint().setAll(buildConfig.getLintMode());
  176. }
  177. if (buildConfig.getLintSpecFile() != null) {
  178. bcelWorld.getLint().setFromProperties(buildConfig.getLintSpecFile());
  179. }
  180. //??? incremental issues
  181. for (Iterator i = buildConfig.getInJars().iterator(); i.hasNext(); ) {
  182. File inJar = (File)i.next();
  183. bcelWeaver.addJarFile(inJar, buildConfig.getOutputDir(),false);
  184. }
  185. for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext(); ) {
  186. File inPathElement = (File)i.next();
  187. bcelWeaver.addJarFile(inPathElement,buildConfig.getOutputDir(),true);
  188. }
  189. if (buildConfig.getSourcePathResources() != null) {
  190. for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext(); ) {
  191. // File resource = (File)i.next();
  192. String resource = (String)i.next();
  193. bcelWeaver.addResource(resource, (File)buildConfig.getSourcePathResources().get(resource), buildConfig.getOutputDir());
  194. // bcelWeaver.addResource(resource, buildConfig.getOutputDir());
  195. }
  196. }
  197. //check for org.aspectj.runtime.JoinPoint
  198. bcelWorld.resolve("org.aspectj.lang.JoinPoint");
  199. }
  200. public World getWorld() {
  201. return bcelWorld;
  202. }
  203. void addAspectClassFilesToWeaver(List addedClassFiles) throws IOException {
  204. for (Iterator i = addedClassFiles.iterator(); i.hasNext(); ) {
  205. UnwovenClassFile classFile = (UnwovenClassFile) i.next();
  206. bcelWeaver.addClassFile(classFile);
  207. }
  208. }
  209. public boolean weaveAndGenerateClassFiles() throws IOException {
  210. handler.handleMessage(MessageUtil.info("weaving"));
  211. if (progressListener != null) progressListener.setText("weaving aspects");
  212. bcelWeaver.setProgressListener(progressListener, 0.5, 0.5/state.addedClassFiles.size());
  213. //!!! doesn't provide intermediate progress during weaving
  214. // XXX add all aspects even during incremental builds?
  215. addAspectClassFilesToWeaver(state.addedClassFiles);
  216. if (buildConfig.isNoWeave()) {
  217. if (buildConfig.getOutputJar() != null) {
  218. bcelWeaver.dumpUnwoven(buildConfig.getOutputJar());
  219. } else {
  220. bcelWeaver.dumpUnwoven();
  221. bcelWeaver.dumpResourcesToOutPath();
  222. }
  223. } else {
  224. if (buildConfig.getOutputJar() != null) {
  225. bcelWeaver.weave(buildConfig.getOutputJar());
  226. } else {
  227. bcelWeaver.weave();
  228. bcelWeaver.dumpResourcesToOutPath();
  229. }
  230. }
  231. if (progressListener != null) progressListener.setProgress(1.0);
  232. return true;
  233. //return messageAdapter.getErrorCount() == 0; //!javaBuilder.notifier.anyErrors();
  234. }
  235. public FileSystem getLibraryAccess(String[] classpaths, String[] filenames) {
  236. String defaultEncoding = (String) buildConfig.getJavaOptions().get(CompilerOptions.OPTION_Encoding);
  237. if ("".equals(defaultEncoding)) //$NON-NLS-1$
  238. defaultEncoding = null; //$NON-NLS-1$
  239. // Bug 46671: We need an array as long as the number of elements in the classpath - *even though* not every
  240. // element of the classpath is likely to be a directory. If we ensure every element of the array is set to
  241. // only look for BINARY, then we make sure that for any classpath element that is a directory, we won't build
  242. // a classpathDirectory object that will attempt to look for source when it can't find binary.
  243. int[] classpathModes = new int[classpaths.length];
  244. for (int i =0 ;i<classpaths.length;i++) classpathModes[i]=ClasspathDirectory.BINARY;
  245. return new FileSystem(classpaths, filenames, defaultEncoding,classpathModes);
  246. }
  247. public IProblemFactory getProblemFactory() {
  248. return new DefaultProblemFactory(Locale.getDefault());
  249. }
  250. /*
  251. * Build the set of compilation source units
  252. */
  253. public CompilationUnit[] getCompilationUnits(String[] filenames, String[] encodings) {
  254. int fileCount = filenames.length;
  255. CompilationUnit[] units = new CompilationUnit[fileCount];
  256. HashtableOfObject knownFileNames = new HashtableOfObject(fileCount);
  257. String defaultEncoding = (String) buildConfig.getJavaOptions().get(CompilerOptions.OPTION_Encoding);
  258. if ("".equals(defaultEncoding)) //$NON-NLS-1$
  259. defaultEncoding = null; //$NON-NLS-1$
  260. for (int i = 0; i < fileCount; i++) {
  261. String encoding = encodings[i];
  262. if (encoding == null)
  263. encoding = defaultEncoding;
  264. units[i] = new CompilationUnit(null, filenames[i], encoding);
  265. }
  266. return units;
  267. }
  268. public String extractDestinationPathFromSourceFile(CompilationResult result) {
  269. ICompilationUnit compilationUnit = result.compilationUnit;
  270. if (compilationUnit != null) {
  271. char[] fileName = compilationUnit.getFileName();
  272. int lastIndex = CharOperation.lastIndexOf(java.io.File.separatorChar, fileName);
  273. if (lastIndex == -1) {
  274. return System.getProperty("user.dir"); //$NON-NLS-1$
  275. }
  276. return new String(CharOperation.subarray(fileName, 0, lastIndex));
  277. }
  278. return System.getProperty("user.dir"); //$NON-NLS-1$
  279. }
  280. public void performCompilation(List files) {
  281. if (progressListener != null) {
  282. compiledCount = 0;
  283. sourceFileCount = files.size();
  284. progressListener.setText("compiling source files");
  285. }
  286. //System.err.println("got files: " + files);
  287. String[] filenames = new String[files.size()];
  288. String[] encodings = new String[files.size()];
  289. //System.err.println("filename: " + this.filenames);
  290. for (int i=0; i < files.size(); i++) {
  291. filenames[i] = ((File)files.get(i)).getPath();
  292. }
  293. List cps = buildConfig.getFullClasspath();
  294. String[] classpaths = new String[cps.size()];
  295. for (int i=0; i < cps.size(); i++) {
  296. classpaths[i] = (String)cps.get(i);
  297. }
  298. //System.out.println("compiling");
  299. INameEnvironment environment = getLibraryAccess(classpaths, filenames);
  300. if (!state.classesFromName.isEmpty()) {
  301. environment = new StatefulNameEnvironment(environment, state.classesFromName);
  302. }
  303. AjCompiler compiler = new AjCompiler(
  304. environment,
  305. DefaultErrorHandlingPolicies.proceedWithAllProblems(),
  306. buildConfig.getJavaOptions(),
  307. getBatchRequestor(),
  308. getProblemFactory());
  309. AjProblemReporter pr =
  310. new AjProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(),
  311. compiler.options, getProblemFactory());
  312. compiler.problemReporter = pr;
  313. AjLookupEnvironment le =
  314. new AjLookupEnvironment(compiler, compiler.options, pr, environment);
  315. EclipseFactory factory = new EclipseFactory(le);
  316. // ew.setLint(bcelWorld.getLint());
  317. // ew.setXnoInline(buildConfig.isXnoInline());
  318. le.factory = factory;
  319. pr.factory = factory;
  320. le.factory.buildManager = this;
  321. compiler.lookupEnvironment = le;
  322. compiler.parser =
  323. new AjParser(
  324. pr,
  325. compiler.options.parseLiteralExpressionsAsConstants);
  326. CompilerOptions options = compiler.options;
  327. options.produceReferenceInfo(true); //TODO turn off when not needed
  328. compiler.compile(getCompilationUnits(filenames, encodings));
  329. // cleanup
  330. environment.cleanup();
  331. }
  332. /*
  333. * Answer the component to which will be handed back compilation results from the compiler
  334. */
  335. public ICompilerRequestor getBatchRequestor() {
  336. return new ICompilerRequestor() {
  337. int lineDelta = 0;
  338. public void acceptResult(CompilationResult compilationResult) {
  339. if (progressListener != null) {
  340. compiledCount++;
  341. progressListener.setProgress((compiledCount/2.0)/sourceFileCount);
  342. progressListener.setText("compiled: " + new String(compilationResult.getFileName()));
  343. }
  344. if (compilationResult.hasProblems() || compilationResult.hasTasks()) {
  345. IProblem[] problems = compilationResult.getAllProblems();
  346. for (int i=0; i < problems.length; i++) {
  347. IMessage message =
  348. EclipseAdapterUtils.makeMessage(compilationResult.compilationUnit, problems[i]);
  349. handler.handleMessage(message);
  350. }
  351. }
  352. outputClassFiles(compilationResult);
  353. }
  354. };
  355. }
  356. private boolean proceedOnError() {
  357. return true; //???
  358. }
  359. public void outputClassFiles(CompilationResult unitResult) {
  360. if (unitResult == null) return;
  361. String sourceFileName = new String(unitResult.fileName);
  362. if (!(unitResult.hasErrors() && !proceedOnError())) {
  363. List unwovenClassFiles = new ArrayList();
  364. Enumeration classFiles = unitResult.compiledTypes.elements();
  365. while (classFiles.hasMoreElements()) {
  366. ClassFile classFile = (ClassFile) classFiles.nextElement();
  367. String filename = new String(classFile.fileName());
  368. filename = filename.replace('/', File.separatorChar) + ".class";
  369. File destinationPath = buildConfig.getOutputDir();
  370. if (destinationPath == null) {
  371. filename = new File(filename).getName();
  372. filename = new File(extractDestinationPathFromSourceFile(unitResult), filename).getPath();
  373. } else {
  374. filename = new File(destinationPath, filename).getPath();
  375. }
  376. //System.out.println("classfile: " + filename);
  377. unwovenClassFiles.add(new UnwovenClassFile(filename, classFile.getBytes()));
  378. }
  379. state.noteClassesFromFile(unitResult, sourceFileName, unwovenClassFiles);
  380. // System.out.println("file: " + sourceFileName);
  381. // for (int i=0; i < unitResult.simpleNameReferences.length; i++) {
  382. // System.out.println("simple: " + new String(unitResult.simpleNameReferences[i]));
  383. // }
  384. // for (int i=0; i < unitResult.qualifiedReferences.length; i++) {
  385. // System.out.println("qualified: " +
  386. // new String(CharOperation.concatWith(unitResult.qualifiedReferences[i], '/')));
  387. // }
  388. } else {
  389. state.noteClassesFromFile(null, sourceFileName, Collections.EMPTY_LIST);
  390. }
  391. }
  392. private void setBuildConfig(AjBuildConfig buildConfig) {
  393. this.buildConfig = buildConfig;
  394. handler.reset();
  395. }
  396. String makeClasspathString() {
  397. if (buildConfig == null || buildConfig.getClasspath() == null) return "";
  398. StringBuffer buf = new StringBuffer();
  399. boolean first = true;
  400. for (Iterator it = buildConfig.getClasspath().iterator(); it.hasNext(); ) {
  401. if (first) { first = false; }
  402. else { buf.append(File.pathSeparator); }
  403. buf.append(it.next().toString());
  404. }
  405. return buf.toString();
  406. }
  407. /**
  408. * This will return null if aspectjrt.jar is present and has the correct version.
  409. * Otherwise it will return a string message indicating the problem.
  410. */
  411. public String checkRtJar(AjBuildConfig buildConfig) {
  412. // omitting dev info
  413. if (Version.text.equals(Version.DEVELOPMENT)) {
  414. // in the development version we can't do this test usefully
  415. // MessageUtil.info(holder, "running development version of aspectj compiler");
  416. return null;
  417. }
  418. if (buildConfig == null || buildConfig.getClasspath() == null) return "no classpath specified";
  419. for (Iterator it = buildConfig.getClasspath().iterator(); it.hasNext(); ) {
  420. File p = new File( (String)it.next() );
  421. if (p.isFile() && p.getName().equals("aspectjrt.jar")) {
  422. try {
  423. String version = null;
  424. Manifest manifest = new JarFile(p).getManifest();
  425. if (manifest == null) {
  426. return "no manifest found in " + p.getAbsolutePath() +
  427. ", expected " + Version.text;
  428. }
  429. Attributes attr = manifest.getAttributes("org/aspectj/lang/");
  430. if (null != attr) {
  431. version = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
  432. if (null != version) {
  433. version = version.trim();
  434. }
  435. }
  436. // assume that users of development aspectjrt.jar know what they're doing
  437. if (Version.DEVELOPMENT.equals(version)) {
  438. // MessageUtil.info(holder,
  439. // "running with development version of aspectjrt.jar in " +
  440. // p.getAbsolutePath());
  441. return null;
  442. } else if (!Version.text.equals(version)) {
  443. return "bad version number found in " + p.getAbsolutePath() +
  444. " expected " + Version.text + " found " + version;
  445. }
  446. } catch (IOException ioe) {
  447. return "bad jar file found in " + p.getAbsolutePath() + " error: " + ioe;
  448. }
  449. return null;
  450. } else {
  451. // might want to catch other classpath errors
  452. }
  453. }
  454. return "couldn't find aspectjrt.jar on classpath, checked: " + makeClasspathString();
  455. }
  456. public String toString() {
  457. StringBuffer buf = new StringBuffer();
  458. buf.append("AjBuildManager(");
  459. buf.append(")");
  460. return buf.toString();
  461. }
  462. public void setStructureModel(IHierarchy structureModel) {
  463. this.structureModel = structureModel;
  464. }
  465. /**
  466. * Returns null if there is no structure model
  467. */
  468. public IHierarchy getStructureModel() {
  469. return structureModel;
  470. }
  471. public IProgressListener getProgressListener() {
  472. return progressListener;
  473. }
  474. public void setProgressListener(IProgressListener progressListener) {
  475. this.progressListener = progressListener;
  476. }
  477. } // class AjBuildManager