/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.lang.ide.core.operations; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.CoreUtil.list; import java.nio.file.Path; import java.text.MessageFormat; import java.util.Map.Entry; import java.util.Optional; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; import melnorme.lang.ide.core.CoreSettings; import melnorme.lang.ide.core.CoreSettings.SettingsField; import melnorme.lang.ide.core.ILangOperationsListener; import melnorme.lang.ide.core.LangCore; import melnorme.lang.ide.core.LangCore_Actual; import melnorme.lang.ide.core.operations.ILangOperationsListener_Default.IToolOperationMonitor; import melnorme.lang.ide.core.operations.ILangOperationsListener_Default.ProcessStartKind; import melnorme.lang.ide.core.operations.ILangOperationsListener_Default.StartOperationOptions; import melnorme.lang.ide.core.operations.build.VariablesResolver; import melnorme.lang.ide.core.operations.build.VariablesResolver.SupplierAdapterVar; import melnorme.lang.ide.core.utils.EclipseUtils; import melnorme.lang.ide.core.utils.ResourceUtils; import melnorme.lang.ide.core.utils.process.AbstractRunProcessTask; import melnorme.lang.ide.core.utils.process.AbstractRunProcessTask.ProcessStartHelper; import melnorme.lang.tooling.commands.CommandInvocation; import melnorme.lang.tooling.common.ops.IOperationMonitor; import melnorme.lang.tooling.toolchain.ops.IToolOperationService; import melnorme.lang.utils.EnvUtils; import melnorme.lang.utils.ProcessUtils; import melnorme.lang.utils.validators.PathValidator; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.collections.Indexable; import melnorme.utilbox.collections.MapAccess; import melnorme.utilbox.concurrency.ICancelMonitor; import melnorme.utilbox.concurrency.OperationCancellation; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.fields.EventSource; import melnorme.utilbox.misc.Location; import melnorme.utilbox.misc.MiscUtil; import melnorme.utilbox.misc.PathUtil; import melnorme.utilbox.process.ExternalProcessHelper.ExternalProcessResult; import melnorme.utilbox.status.StatusException; import melnorme.utilbox.status.StatusLevel; /** * Abstract class for running external tools and notifying interested listeners (normally the UI only). */ public abstract class ToolManager extends EventSource<ILangOperationsListener> implements IStatusMessageHandler { protected final CoreSettings settings; public ToolManager(CoreSettings settings) { this.settings = assertNotNull(settings); } public void shutdownNow() { } /* ----------------- ----------------- */ public Path getSDKToolPath(IProject project) throws CommonException { return settings.SDK_LOCATION.getValue(project); } public PathValidator getSDKToolPathValidator() { return settings.getSDKLocationValidator(); } /* ----------------- ----------------- */ protected final IStringVariableManager globalVarManager = VariablesPlugin.getDefault().getStringVariableManager(); public VariablesResolver getVariablesManager(Optional<IProject> project) { project = MiscUtil.toOptional(project); VariablesResolver variablesResolver = new VariablesResolver(globalVarManager); setupVariableResolver(variablesResolver, project); return variablesResolver; } protected void setupVariableResolver(VariablesResolver variablesResolver, Optional<IProject> project) { SettingsField<Path> pref = LangCore.settings().SDK_LOCATION; variablesResolver.putDynamicVar(new SupplierAdapterVar( LangCore_Actual.VAR_NAME_SdkToolPath, LangCore_Actual.VAR_NAME_SdkToolPath_DESCRIPTION, pref.getRawValueSupplier(project), pref.getValidator_toString()) ); } /* ----------------- ----------------- */ public ProcessBuilder createSDKProcessBuilder(IProject project, String... sdkOptions) throws CommonException { Path sdkToolPath = getSDKToolPath(project); return createToolProcessBuilder(project, sdkToolPath, sdkOptions); } public final ProcessBuilder createToolProcessBuilder(IProject project, Path toolPath, String... toolArguments) throws CommonException { Location projectLocation = project == null ? null : ResourceUtils.getProjectLocation2(project); ArrayList2<String> commandLine = ProcessUtils.createCommandLine(toolPath, toolArguments); return createToolProcessBuilder(commandLine, projectLocation); } public ProcessBuilder createToolProcessBuilder(Indexable<String> commandLine, Location workingDir) { return modifyToolProcessBuilder(ProcessUtils.createProcessBuilder(commandLine, workingDir)); } public ProcessBuilder modifyToolProcessBuilder(ProcessBuilder pb) { Path cmdExePath = PathUtil.createPathOrNull(pb.command().get(0)); EnvUtils.addCmdDirToPathEnv(cmdExePath, pb); return pb; } public ProcessBuilder createSimpleProcessBuilder(IProject project, String... commands) throws CommonException { Location workingDir = project == null ? null : ResourceUtils.getProjectLocation2(project); return ProcessUtils.createProcessBuilder(list(commands), workingDir); } /* ----------------- ----------------- */ public void logAndNotifyError(String title, StatusException ce) { logAndNotifyError(title, title, ce); } public void logAndNotifyError(String msgId, String title, StatusException ce) { LangCore.logError(title, ce); notifyMessage(msgId, ce.getSeverity().toStatusLevel(), title, ce.getMessage()); } @Override public void notifyMessage(String msgId, StatusLevel statusLevel, String title, String message) { for(ILangOperationsListener listener : getListeners()) { listener.notifyMessage(msgId, statusLevel, title, message); } } /* ----------------- ----------------- */ public IToolOperationMonitor startNewBuildOperation() { return startNewBuildOperation(false); } public IToolOperationMonitor startNewBuildOperation(boolean explicitConsoleNotify) { return startNewOperation(ProcessStartKind.BUILD, true, explicitConsoleNotify); } public IToolOperationMonitor startNewOperation(ProcessStartKind kind, boolean clearConsole, boolean activateConsole) { return startNewOperation(new StartOperationOptions(kind, clearConsole, activateConsole)); } public IToolOperationMonitor startNewOperation(StartOperationOptions options) { AggregatedToolOperationMonitor aggregatedHandlers = new AggregatedToolOperationMonitor(); for(ILangOperationsListener processListener : getListeners()) { IToolOperationMonitor handler = processListener.beginOperation(options); aggregatedHandlers.monitors.add(handler); } return aggregatedHandlers; } public static class AggregatedToolOperationMonitor implements IToolOperationMonitor { public final ArrayList2<IToolOperationMonitor> monitors = new ArrayList2<>(); public AggregatedToolOperationMonitor() { } @Override public void handleProcessStart(String prefixText, String suffixText, ProcessBuilder pb, ProcessStartHelper processStartHelper) { for (IToolOperationMonitor monitor : monitors) { monitor.handleProcessStart(prefixText, suffixText, pb, processStartHelper); } } @Override public void writeInfoMessage(String operationMessage) { for (IToolOperationMonitor monitor : monitors) { monitor.writeInfoMessage(operationMessage); } } @Override public void activate() { for (IToolOperationMonitor monitor : monitors) { monitor.activate(); } } } /* ----------------- ----------------- */ public final RunToolTask newRunBuildToolOperation(ProcessBuilder pb, IOperationMonitor om) { IToolOperationMonitor opHandler = startNewBuildOperation(); return newRunProcessTask(opHandler, pb, "clean", null, om); } public RunToolTask newRunProcessTask( IToolOperationMonitor opMonitor, ProcessBuilder pb, String buildTargetName, CommandInvocation buildCommand, IOperationMonitor om ) { String prefixText; if(buildTargetName != null) { prefixText = MessageFormat.format(">> Running `{0}` with: ", buildTargetName); } else { prefixText = ">> Running: "; } String suffixText = ""; if(buildCommand != null && buildCommand.getEnvironmentVars() != null) { MapAccess<String, String> envVars = buildCommand.getEnvironmentVars(); for (Entry<String, String> entry : envVars.entrySet()) { suffixText += "\n " + entry.getKey() + "=" + entry.getValue(); } } return new RunToolTask(opMonitor, prefixText, suffixText, pb, om); } public class RunToolTask extends AbstractRunProcessTask { protected final IToolOperationMonitor opMonitor; protected final String prefixText; protected final String suffixText; public RunToolTask(IToolOperationMonitor opMonitor, ProcessBuilder pb, ICancelMonitor cm) { this(opMonitor, null, null, pb, cm); } public RunToolTask(IToolOperationMonitor opMonitor, String prefixText, String suffixText, ProcessBuilder pb, ICancelMonitor cm) { super(pb, cm); this.prefixText = prefixText; this.suffixText = suffixText; this.opMonitor = assertNotNull(opMonitor); } @Override protected void handleProcessStartResult(ProcessStartHelper psh) { opMonitor.handleProcessStart(prefixText, suffixText, pb, psh); } } /* ----------------- ----------------- */ public ExternalProcessResult runEngineTool(ProcessBuilder pb, String processInput, IProgressMonitor pm) throws CommonException, OperationCancellation { return runEngineTool(pb, processInput, EclipseUtils.cm(pm)); } public final ExternalProcessResult runEngineTool(ProcessBuilder pb, String processInput, ICancelMonitor cm) throws CommonException, OperationCancellation { IToolOperationMonitor opMonitor = startNewOperation(ProcessStartKind.ENGINE_TOOLS, false, false); return new RunToolTask(opMonitor, pb, cm).runProcess(processInput); } /* ----------------- ----------------- */ /** * Helper to start engine client processes in the tool manager. */ public class ToolManagerEngineToolRunner implements IToolOperationService { @Override public ExternalProcessResult runProcess(ProcessBuilder pb, String processInput, ICancelMonitor cm) throws CommonException, OperationCancellation { return runEngineTool(pb, processInput, cm); } @Override public void logStatus(StatusException statusException) { LangCore.logStatusException(statusException); } } public IToolOperationService getEngineToolsOperationService() { return new ToolManagerEngineToolRunner(); } }