| /******************************************************************************* |
| * Copyright (c) 2015, 2016 QNX Software Systems and others. |
| * 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 |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.qt.core.build; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.core.ConsoleOutputStream; |
| import org.eclipse.cdt.core.ErrorParserManager; |
| import org.eclipse.cdt.core.IConsoleParser; |
| import org.eclipse.cdt.core.build.CBuildConfiguration; |
| import org.eclipse.cdt.core.build.ICBuildConfiguration; |
| import org.eclipse.cdt.core.build.IToolChain; |
| import org.eclipse.cdt.core.envvar.IEnvironmentVariable; |
| import org.eclipse.cdt.core.model.ICModelMarker; |
| import org.eclipse.cdt.core.parser.ExtendedScannerInfo; |
| import org.eclipse.cdt.core.parser.IExtendedScannerInfo; |
| import org.eclipse.cdt.core.parser.IScannerInfo; |
| import org.eclipse.cdt.core.resources.IConsole; |
| import org.eclipse.cdt.internal.qt.core.Activator; |
| import org.eclipse.cdt.qt.core.IQtBuildConfiguration; |
| import org.eclipse.cdt.qt.core.IQtInstall; |
| import org.eclipse.cdt.qt.core.IQtInstallManager; |
| import org.eclipse.core.resources.IBuildConfiguration; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.osgi.service.prefs.BackingStoreException; |
| import org.osgi.service.prefs.Preferences; |
| |
| public class QtBuildConfiguration extends CBuildConfiguration implements ICBuildConfiguration, IQtBuildConfiguration { |
| |
| private static final String QTINSTALL_NAME = "cdt.qt.install.name"; //$NON-NLS-1$ |
| private static final String LAUNCH_MODE = "cdt.qt.launchMode"; //$NON-NLS-1$ |
| |
| private final IQtInstall qtInstall; |
| private final String launchMode; |
| private Map<String, String> properties; |
| |
| public QtBuildConfiguration(IBuildConfiguration config, String name) { |
| super(config, name); |
| |
| Preferences settings = getSettings(); |
| String installName = settings.get(QTINSTALL_NAME, ""); //$NON-NLS-1$ |
| if (!installName.isEmpty()) { |
| IQtInstallManager manager = Activator.getService(IQtInstallManager.class); |
| qtInstall = manager.getInstall(Paths.get(installName)); |
| } else { |
| qtInstall = null; |
| } |
| |
| launchMode = settings.get(LAUNCH_MODE, ""); //$NON-NLS-1$ |
| } |
| |
| QtBuildConfiguration(IBuildConfiguration config, String name, IToolChain toolChain, IQtInstall qtInstall, |
| String launchMode) |
| throws CoreException { |
| super(config, name, toolChain); |
| this.qtInstall = qtInstall; |
| this.launchMode = launchMode; |
| |
| Preferences settings = getSettings(); |
| settings.put(QTINSTALL_NAME, qtInstall.getQmakePath().toString()); |
| settings.put(LAUNCH_MODE, launchMode); |
| try { |
| settings.flush(); |
| } catch (BackingStoreException e) { |
| Activator.log(e); |
| } |
| } |
| |
| @Override |
| public <T> T getAdapter(Class<T> adapter) { |
| return super.getAdapter(adapter); |
| } |
| |
| public IQtInstall getQtInstall() { |
| return qtInstall; |
| } |
| |
| @Override |
| public String getLaunchMode() { |
| return launchMode; |
| } |
| |
| @Override |
| public Path getQmakeCommand() { |
| return qtInstall.getQmakePath(); |
| } |
| |
| @Override |
| public String getQmakeConfig() { |
| switch (launchMode) { |
| case "run": //$NON-NLS-1$ |
| return "CONFIG+=release"; //$NON-NLS-1$ |
| case "debug": //$NON-NLS-1$ |
| return "CONFIG+=debug"; //$NON-NLS-1$ |
| default: |
| // TODO probably need an extension point for guidance |
| return null; |
| } |
| } |
| |
| public Path getProjectFile() { |
| File projectDir = getProject().getLocation().toFile(); |
| File[] proFiles = projectDir.listFiles((dir, name) -> name.endsWith(".pro")); //$NON-NLS-1$ |
| if (proFiles.length > 0) { |
| // TODO what if there are more than one. |
| return proFiles[0].toPath(); |
| } else { |
| return null; |
| } |
| } |
| |
| @Override |
| public Path getProgramPath() throws CoreException { |
| String projectName = getProject().getName(); |
| switch (Platform.getOS()) { |
| case Platform.OS_MACOSX: |
| // TODO this is mac local specific and really should be |
| // in the config |
| // TODO also need to pull the app name out of the pro |
| // file name |
| Path appFolder = getBuildDirectory().resolve(projectName + ".app"); //$NON-NLS-1$ |
| Path contentsFolder = appFolder.resolve("Contents"); //$NON-NLS-1$ |
| Path macosFolder = contentsFolder.resolve("MacOS"); //$NON-NLS-1$ |
| return macosFolder.resolve(projectName); |
| case Platform.OS_WIN32: { |
| String subdir = "run".equals(launchMode) ? "release" : "debug"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return getBuildDirectory().resolve(subdir).resolve(projectName + ".exe"); //$NON-NLS-1$ |
| } |
| default: |
| Path releaseFolder = getBuildDirectory().resolve("release"); //$NON-NLS-1$ |
| return releaseFolder.resolve(projectName); |
| } |
| } |
| |
| public String getProperty(String key) { |
| if (properties == null) { |
| List<String> cmd = new ArrayList<>(); |
| cmd.add(getQmakeCommand().toString()); |
| cmd.add("-E"); //$NON-NLS-1$ |
| |
| String config = getQmakeConfig(); |
| if (config != null) { |
| cmd.add(config); |
| } |
| |
| cmd.add(getProjectFile().toString()); |
| |
| try { |
| ProcessBuilder processBuilder = new ProcessBuilder(cmd) |
| .directory(getProjectFile().getParent().toFile()); |
| setBuildEnvironment(processBuilder.environment()); |
| Process proc = processBuilder.start(); |
| try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) { |
| properties = new HashMap<>(); |
| for (String line = reader.readLine(); line != null; line = reader.readLine()) { |
| int i = line.indexOf('='); |
| if (i >= 0) { |
| String k = line.substring(0, i); |
| String v = line.substring(i + 1); |
| properties.put(k.trim(), v.trim()); |
| } |
| } |
| } |
| } catch (IOException e) { |
| Activator.log(e); |
| } |
| } |
| |
| return properties != null ? properties.get(key) : null; |
| } |
| |
| @Override |
| public IEnvironmentVariable getVariable(String name) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public IEnvironmentVariable[] getVariables() { |
| // TODO |
| return new IEnvironmentVariable[0]; |
| } |
| |
| @Override |
| public IScannerInfo getScannerInformation(IResource resource) { |
| IProject project = resource.getProject(); |
| IQtInstall qtInstall = getQtInstall(); |
| |
| String cxx = getProperty("QMAKE_CXX"); //$NON-NLS-1$ |
| if (cxx == null) { |
| Activator.log("No QMAKE_CXX for " + qtInstall.getSpec()); //$NON-NLS-1$ |
| return null; |
| } |
| String[] cxxSplit = cxx.split(" "); //$NON-NLS-1$ |
| Path command = Paths.get(cxxSplit[0]); |
| |
| List<String> args = new ArrayList<>(); |
| for (int i = 1; i < cxxSplit.length; ++i) { |
| args.add(cxxSplit[i]); |
| } |
| args.addAll(Arrays.asList(getProperty("QMAKE_CXXFLAGS").split(" "))); //$NON-NLS-1$ //$NON-NLS-2$ |
| args.add("-o"); //$NON-NLS-1$ |
| args.add("-"); //$NON-NLS-1$ |
| |
| String srcFile; |
| if (resource instanceof IFile) { |
| srcFile = resource.getLocation().toOSString(); |
| // Only add file if it's an IFile |
| args.add(srcFile); |
| } else { |
| // Doesn't matter, the toolchain will create a tmp file for this |
| srcFile = "scannerInfo.cpp"; //$NON-NLS-1$ |
| } |
| |
| String[] includePaths = getProperty("INCLUDEPATH").split(" "); //$NON-NLS-1$ //$NON-NLS-2$ |
| for (int i = 0; i < includePaths.length; ++i) { |
| Path path = Paths.get(includePaths[i]); |
| if (!path.isAbsolute()) { |
| try { |
| includePaths[i] = getBuildDirectory().resolve(path).toString(); |
| } catch (CoreException e) { |
| Activator.log(e); |
| } |
| } |
| } |
| |
| IExtendedScannerInfo baseScannerInfo = new ExtendedScannerInfo(null, includePaths); |
| try { |
| return getToolChain().getScannerInfo(getBuildConfiguration(), command, |
| args.toArray(new String[args.size()]), baseScannerInfo, resource, |
| getBuildContainer().getLocationURI()); |
| } catch (CoreException e) { |
| Activator.log(e); |
| return null; |
| } |
| } |
| |
| @Override |
| public IProject[] build(int kind, Map<String, String> args, IConsole console, IProgressMonitor monitor) |
| throws CoreException { |
| IProject project = getProject(); |
| try { |
| project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| |
| ConsoleOutputStream errStream = console.getErrorStream(); |
| ConsoleOutputStream outStream = console.getOutputStream(); |
| |
| Path makeCommand = getMakeCommand(); |
| if (makeCommand == null) { |
| errStream.write("'make' not found.\n"); |
| return null; |
| } |
| |
| try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this, |
| getToolChain().getErrorParserIds())) { |
| Path buildDir = getBuildDirectory(); |
| if (!buildDir.resolve("Makefile").toFile().exists()) { //$NON-NLS-1$ |
| // Need to run qmake |
| List<String> command = new ArrayList<>(); |
| command.add(getQmakeCommand().toString()); |
| |
| String config = getQmakeConfig(); |
| if (config != null) { |
| command.add(config); |
| } |
| |
| IFile projectFile = project.getFile(project.getName() + ".pro"); //$NON-NLS-1$ |
| command.add(projectFile.getLocation().toOSString()); |
| |
| ProcessBuilder processBuilder = new ProcessBuilder(command) |
| .directory(getBuildDirectory().toFile()); |
| setBuildEnvironment(processBuilder.environment()); |
| Process process = processBuilder.start(); |
| |
| StringBuffer msg = new StringBuffer(); |
| for (String arg : command) { |
| msg.append(arg).append(' '); |
| } |
| msg.append('\n'); |
| outStream.write(msg.toString()); |
| |
| // TODO qmake error parser |
| watchProcess(process, new IConsoleParser[0], console); |
| } |
| |
| // run make |
| ProcessBuilder processBuilder = new ProcessBuilder(makeCommand.toString()).directory(buildDir.toFile()); |
| setBuildEnvironment(processBuilder.environment()); |
| Process process = processBuilder.start(); |
| outStream.write(makeCommand.toString() + '\n'); |
| watchProcess(process, new IConsoleParser[] { epm }, console); |
| } |
| |
| getProject().refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| return new IProject[] { project }; |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, Activator.ID, "Building " + project.getName(), e)); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| public void clean(IConsole console, IProgressMonitor monitor) throws CoreException { |
| IProject project = getProject(); |
| try { |
| project.deleteMarkers(ICModelMarker.C_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| |
| ConsoleOutputStream errStream = console.getErrorStream(); |
| ConsoleOutputStream outStream = console.getOutputStream(); |
| |
| Path makeCommand = getMakeCommand(); |
| if (makeCommand == null) { |
| errStream.write("'make' not found.\n"); |
| return; |
| } |
| |
| Path buildDir = getBuildDirectory(); |
| |
| try (ErrorParserManager epm = new ErrorParserManager(project, getBuildDirectoryURI(), this, |
| getToolChain().getErrorParserIds())) { |
| // run make |
| ProcessBuilder processBuilder = new ProcessBuilder(makeCommand.toString(), "clean") //$NON-NLS-1$ |
| .directory(buildDir.toFile()); |
| setBuildEnvironment(processBuilder.environment()); |
| Process process = processBuilder.start(); |
| outStream.write(makeCommand.toString() + "clean\n"); //$NON-NLS-1$ |
| watchProcess(process, new IConsoleParser[] { epm }, console); |
| } |
| |
| project.refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| } catch (IOException e) { |
| throw new CoreException(new Status(IStatus.ERROR, Activator.ID, "Cleaning " + project.getName(), e)); //$NON-NLS-1$ |
| } |
| } |
| |
| public Path getMakeCommand() { |
| Path makeCommand = findCommand("make"); //$NON-NLS-1$ |
| if (makeCommand == null) { |
| makeCommand = findCommand("mingw32-make"); //$NON-NLS-1$ |
| } |
| return makeCommand; |
| } |
| |
| } |