Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: efa4accea72c2596d56548755b70a03c13cf4718 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
package org.eclipse.cdt.internal.core;

/**********************************************************************
 * Copyright (c) 2002,2003 Rational Software Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: 
 * IBM Rational Software - Initial API and implementation
 * **********************************************************************/

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CommandLauncher;
import org.eclipse.cdt.core.ConsoleOutputStream;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.build.managed.IManagedBuildInfo;
import org.eclipse.cdt.core.build.managed.ManagedBuildManager;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.resources.ACBuilder;
import org.eclipse.cdt.core.resources.IConsole;
import org.eclipse.cdt.core.resources.MakeUtil;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SubProgressMonitor;

public class GeneratedMakefileBuilder extends ACBuilder {
	// String constants
	private static final String MESSAGE = "MakeBuilder.message";	//$NON-NLS-1$
	private static final String BUILD_ERROR = MESSAGE + ".error";	//$NON-NLS-1$
	private static final String BUILD_FINISHED = MESSAGE + ".finished";	//$NON-NLS-1$
	private static final String INCREMENTAL = MESSAGE + ".incremental";	//$NON-NLS-1$
	private static final String MAKE = MESSAGE + ".make";	//$NON-NLS-1$
	private static final String REBUILD = MESSAGE + ".rebuild";	//$NON-NLS-1$
	private static final String START = MESSAGE + ".starting";	//$NON-NLS-1$

	// Status codes
	public static final int EMPTY_PROJECT_BUILD_ERROR = 1;

	// Local variables
	protected List resourcesToBuild;
	protected List ruleList;

	
	public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
		private boolean buildNeeded = false;

		public boolean visit(IResourceDelta delta) throws CoreException {
			IResource resource = delta.getResource();
			// If the project has changed, then a build is needed and we can stop
			if (resource != null && resource.getProject() == getProject()) {
				buildNeeded = true;
				return false;
			}

			return true;
		}

		public boolean shouldBuild() {
			return buildNeeded;
		}
	}

	/**
	 * 
	 */
	public GeneratedMakefileBuilder() {
		super();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
	 */
	protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
		String statusMsg = CCorePlugin.getFormattedString(START, getProject().getName());
		if (statusMsg != null) {
			monitor.subTask(statusMsg);
		}

		if (kind == IncrementalProjectBuilder.FULL_BUILD) {
			fullBuild(monitor);
		}
		else {
			// Create a delta visitor to make sure we should be rebuilding
			ResourceDeltaVisitor visitor = new ResourceDeltaVisitor();
			IResourceDelta delta = getDelta(getProject());
			if (delta == null) {
				fullBuild(monitor);
			}
			else {
				delta.accept(visitor);
				if (visitor.shouldBuild()) {
					incrementalBuild(delta, monitor);
				}
			}
		}
		// Checking to see if the user cancelled the build
		checkCancel(monitor);

		// Ask build mechanism to compute deltas for project dependencies next time
		return getProject().getReferencedProjects();
	}

	/**
	 * Check whether the build has been canceled. Cancellation requests 
	 * propagated to the caller by throwing <code>OperationCanceledException</code>.
	 * 
	 * @see org.eclipse.core.runtime.OperationCanceledException#OperationCanceledException()
	 */
	public void checkCancel(IProgressMonitor monitor) {
		if (monitor != null && monitor.isCanceled()) {
			throw new OperationCanceledException();
		}
	}

	/**
	 * @param monitor
	 */
	protected void fullBuild(IProgressMonitor monitor) throws CoreException {
		// Always need one of these bad boys
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		
		// We also need one of these ...
		IProject currentProject = getProject();
		if (currentProject == null) {
			// Flag some sort of error and bail
			return;
		}

		// Regenerate the makefiles for any managed projects this project depends on
		IProject[] deps = currentProject.getReferencedProjects();
		for (int i = 0; i < deps.length; i++) {
			IProject depProject = deps[i];
			if (ManagedBuildManager.manages(depProject)) {
				IManagedBuildInfo depInfo = ManagedBuildManager.getBuildInfo(depProject);
				MakefileGenerator generator = new MakefileGenerator(depProject, depInfo, monitor);
				try {
					generator.regenerateMakefiles();		
				} catch (CoreException e) {
					// This may be an empty project exception
					if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) {
						// Just keep looking for other projects
						continue;
					}
				}
			}
		}

		// Need to report status to the user
		String statusMsg = CCorePlugin.getFormattedString(REBUILD, currentProject.getName());
		monitor.subTask(statusMsg);

		// Regenerate the makefiles for this project
		IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject());
		MakefileGenerator generator = new MakefileGenerator(currentProject, info, monitor);		
		try {
			generator.regenerateMakefiles();
		} catch (CoreException e) {
			// See if this is an empty project
			if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) {
				monitor.worked(1);
				return;
			}
		}
		IPath topBuildDir = generator.getTopBuildDir();
		
		// Now call make
		invokeMake(true, topBuildDir.removeFirstSegments(1), info, monitor);
		
		monitor.worked(1);		
	}

	/**
	 * @param makefilePath
	 * @param info
	 * @return
	 */
	protected String[] getMakeTargets() {
		List args = new ArrayList();
		// Add each target
		String sessionTarget = MakeUtil.getSessionTarget(getProject());
		StringTokenizer tokens = new StringTokenizer(sessionTarget);
		while (tokens.hasMoreTokens()) {
			args.add(tokens.nextToken().trim());
		}
		if (args.isEmpty()) {
			args.add("all");
		}
		return (String[])args.toArray(new String[args.size()]);
	}
	
	/**
	 * @return
	 */
	protected List getResourcesToBuild() {
		if (resourcesToBuild == null) {
			resourcesToBuild = new ArrayList();
		}
		return resourcesToBuild;
	}

	/**
	 * @return
	 */
	protected List getRuleList() {
		if (ruleList == null) {
			ruleList = new ArrayList();
		}
		return ruleList;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.core.resources.ACBuilder#getWorkingDirectory()
	 */
	public IPath getWorkingDirectory() {
		IProject currProject = getProject();
		IPath workingDirectory = new Path(MakeUtil.getSessionBuildDir((IResource) currProject));
		if (workingDirectory.isEmpty())
			workingDirectory = currProject.getLocation();
		return workingDirectory;
	}

	/**
	 * @param delta
	 * @param monitor
	 */
	protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException {
		// Rebuild the resource tree in the delta
		IProject currentProject = getProject();
		String statusMsg = null;
		
		// Need to report status to the user
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}
		statusMsg = CCorePlugin.getFormattedString(INCREMENTAL, currentProject.getName());
		monitor.subTask(statusMsg);
		
		// Ask the makefile generator to generate any makefiles needed to build delta
		IManagedBuildInfo info = ManagedBuildManager.getBuildInfo(getProject());
		MakefileGenerator generator = new MakefileGenerator(currentProject, info, monitor);
		try {
			generator.generateMakefiles(delta);
		} catch (CoreException e) {
			if (e.getStatus().getCode() == GeneratedMakefileBuilder.EMPTY_PROJECT_BUILD_ERROR) {
				// There is nothing to build so bail
				monitor.worked(1);
				return;		
			} else {
				throw e;
			}
		}	

		// Run the build if there is any relevant change
		if (generator.shouldRunBuild()) {
			IPath buildDir = new Path(info.getConfigurationName());
			invokeMake(false, buildDir, info, monitor);
		}
		
		monitor.worked(1);		
	}

	protected void invokeMake(boolean fullBuild, IPath buildDir, IManagedBuildInfo info, IProgressMonitor monitor) {
		boolean isCanceled = false;
		IProject currentProject = getProject();
		SubProgressMonitor subMonitor = null;
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		// Flag to the user that make is about to be called
		IPath makeCommand = new Path(info.getMakeCommand()); 
		String[] msgs = new String[2];
		msgs[0] = info.getMakeCommand();
		msgs[1] = currentProject.getName();
		String statusMsg = CCorePlugin.getFormattedString(MAKE, msgs);
		if (statusMsg != null) {
			monitor.subTask(statusMsg);
		}

		// Get a build console for the project
		IConsole console = null;
		ConsoleOutputStream consoleOutStream = null;
		IWorkspace workspace = null;
		IMarker[] markers = null;
		try {
			console = CCorePlugin.getDefault().getConsole();
			console.start(currentProject);
			consoleOutStream = console.getOutputStream();

			// Remove all markers for this project
			workspace = currentProject.getWorkspace();
			markers = currentProject.findMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
			if (markers != null) {
				workspace.deleteMarkers(markers);
			}
		} catch (CoreException e) {
		}

		IPath workingDirectory = getWorkingDirectory().append(buildDir);

		// Get the arguments to be passed to make from build model
		String[] makeTargets = getMakeTargets();
		
		// Get a launcher for the make command
		String errMsg = null;
		CommandLauncher launcher = new CommandLauncher();
		launcher.showCommand(true);

		// Set the environmennt, some scripts may need the CWD var to be set.
		Properties props = launcher.getEnvironment();
		props.put("CWD", workingDirectory.toOSString());
		props.put("PWD", workingDirectory.toOSString());
		String[] env = null;
		ArrayList envList = new ArrayList();
		Enumeration names = props.propertyNames();
		if (names != null) {
			while (names.hasMoreElements()) {
				String key = (String) names.nextElement();
				envList.add(key + "=" + props.getProperty(key));
			}
			env = (String[]) envList.toArray(new String[envList.size()]);
		}
		
		// Hook up an error parser
		ErrorParserManager epm = new ErrorParserManager(this);
		epm.setOutputStream(consoleOutStream);
		OutputStream stdout = epm.getOutputStream();
		OutputStream stderr = epm.getOutputStream();
		
		// Launch make
		Process proc = launcher.execute(makeCommand, makeTargets, env, workingDirectory);
		if (proc != null) {
			try {
				// Close the input of the Process explicitely.
				// We will never write to it.
				proc.getOutputStream().close();
			} catch (IOException e) {
			}
			subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
			if (launcher.waitAndRead(stdout, stderr, subMonitor) != CommandLauncher.OK) {
				errMsg = launcher.getErrorMessage();
			
				isCanceled = monitor.isCanceled();
				monitor.setCanceled(false);
				subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
				subMonitor.subTask("Refresh From Local");

				try {
					currentProject.refreshLocal(IResource.DEPTH_INFINITE, subMonitor);
				} catch (CoreException e) {
				}

				subMonitor = new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN);
				subMonitor.subTask("Parsing");
			} else {
					errMsg = launcher.getErrorMessage();
			}
			
			
			// Report either the success or failure of our mission
			StringBuffer buf = new StringBuffer();
			if (errMsg != null && errMsg.length() > 0) {
				String errorDesc = CCorePlugin.getResourceString(BUILD_ERROR);
				buf.append(errorDesc);
				buf.append(System.getProperty("line.separator", "\n"));
				buf.append("(").append(errMsg).append(")");
			}
			else {
				// Report a successful build
				String successMsg = CCorePlugin.getFormattedString(BUILD_FINISHED, currentProject.getName());
				buf.append(successMsg);
				buf.append(System.getProperty("line.separator", "\n"));
			}
			// Write your message on the pavement
			try {
				consoleOutStream.write(buf.toString().getBytes());
				consoleOutStream.flush();
				stdout.close();
				stderr.close();				
			} catch (IOException e) {
			}

			epm.reportProblems();

			subMonitor.done();
			monitor.setCanceled(isCanceled);
		}
		monitor.done();
	}

}

Back to the top