/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) 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
*
* Contributors:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.core.toolinfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IPath;
import de.innot.avreclipse.PluginIDs;
import de.innot.avreclipse.core.IMCUProvider;
import de.innot.avreclipse.core.paths.AVRPath;
import de.innot.avreclipse.core.paths.AVRPathProvider;
import de.innot.avreclipse.core.paths.IPathProvider;
import de.innot.avreclipse.core.util.AVRMCUidConverter;
/**
* This class provides some information about the used gcc compiler in the toolchain.
* <p>
* It can return a list of all supported target mcus.
* </p>
*
* @author Thomas Holland
* @since 2.1
*/
public class GCC extends BaseToolInfo implements IMCUProvider {
private static final String TOOL_ID = PluginIDs.PLUGIN_TOOLCHAIN_TOOL_COMPILER;
private static GCC instance = null;
private Map<String, String> fMCUmap = null;
private IPath fCurrentPath = null;
private final IPathProvider fPathProvider = new AVRPathProvider(AVRPath.AVRGCC);
/**
* Get an instance of this Tool.
*/
public static GCC getDefault() {
if (instance == null)
instance = new GCC();
return instance;
}
private GCC() {
// Let the superclass get the command name
super(TOOL_ID);
}
/*
* (non-Javadoc)
*
* @see de.innot.avreclipse.core.toolinfo.IToolInfo#getToolPath()
*/
@Override
public IPath getToolPath() {
IPath path = fPathProvider.getPath();
return path.append(getCommandName());
}
/*
* (non-Javadoc)
*
* @see de.innot.avreclipse.core.IMCUProvider#getMCUInfo(java.lang.String)
*/
public String getMCUInfo(String mcuid) {
try {
Map<String, String> internalmap = loadMCUList();
return internalmap.get(mcuid);
} catch (IOException e) {
return null;
}
}
/*
* (non-Javadoc)
*
* @see de.innot.avreclipse.core.IMCUProvider#getMCUList()
*/
public Set<String> getMCUList() throws IOException {
Map<String, String> internalmap = loadMCUList();
Set<String> idlist = internalmap.keySet();
return new HashSet<String>(idlist);
}
/*
* (non-Javadoc)
*
* @see de.innot.avreclipse.core.IMCUProvider#hasMCU(java.lang.String)
*/
public boolean hasMCU(String mcuid) {
try {
Map<String, String> internalmap = loadMCUList();
return internalmap.containsKey(mcuid);
} catch (IOException e) {
return false;
}
}
/**
* @return Map <mcu id, UI name> of all supported MCUs
*/
private Map<String, String> loadMCUList() throws IOException {
if (!getToolPath().equals(fCurrentPath)) {
// toolpath has changed, reload the list
fMCUmap = null;
fCurrentPath = getToolPath();
}
if (fMCUmap != null) {
// return stored map
return fMCUmap;
}
fMCUmap = new HashMap<String, String>();
// Execute avr-gcc with the "--target-help" option and parse the
// output
// Also add -Wa,-mlist-devices so that it works in more recent versions
// Of GCC and AVR CrossPack in Mac OS X
List<String> stdout = runCommand("-Wa,-mlist-devices", "--target-help");
if (stdout == null) {
// Return empty map on failures
return fMCUmap;
}
// The parsing, if you can call it that, it done by looking at all the
// lines starting with " at" or " avr". Then the line is split into the
// mcu ids.
for (String line : stdout) {
if (!line.startsWith(" ")) {
continue;
}
if (line.trim().startsWith("at") ||
line.trim().startsWith("avr")) {
String[] names = line.trim().split(" ");
for (String mcuid : names) {
String mcuname = AVRMCUidConverter.id2name(mcuid);
if (mcuname == null) {
// some mcuid are generic and should not be
// included
continue;
}
fMCUmap.put(mcuid, mcuname);
}
}
}
return fMCUmap;
}
/**
* Get the command name and the current version of GCC.
* <p>
* The name comes from the buildDefinition. The version is gathered by executing with the "-v"
* option and parsing the output.
* </p>
*
* @return <code>String</code> with the command name and version
* @throws IOException
* if the avr-gcc command could not be executed.
*/
public String getNameAndVersion() throws IOException {
// Execute avr-gcc with the "-v" option and parse the
// output
List<String> stdout = runCommand("-v");
if (stdout == null) {
// Return default name on failures
return getCommandName() + " n/a";
}
// look for a line matching "gcc version TheVersionNumber"
Pattern mcuPat = Pattern.compile("gcc version\\s*(.*)");
Matcher m;
for (String line : stdout) {
m = mcuPat.matcher(line);
if (!m.matches()) {
continue;
}
return getCommandName() + " " + m.group(1);
}
// could not read the version from the output, probably the regex has a
// mistake. Return a reasonable default.
return getCommandName() + "?.?";
}
/**
* Runs the GCC with the given arguments.
* <p>
* The Output of stdout and stderr are merged and returned in a <code>List<String></code>.
* </p>
* <p>
* If the command fails to execute an entry is written to the log and <code>null</code> is
* returned
*
* @param arguments
* Zero or more arguments for gcc
* @throws IOException
* if the command could not be executed
* @return A list of all output lines, or <code>null</code> if the command could not be
* launched.
*/
private List<String> runCommand(String... arguments) throws IOException {
String command = getToolPath().toOSString();
List<String> arglist = new ArrayList<String>(1);
for (String arg : arguments) {
arglist.add(arg);
}
ExternalCommandLauncher gcc = new ExternalCommandLauncher(command, arglist);
gcc.redirectErrorStream(true);
gcc.launch();
List<String> stdout = gcc.getStdOut();
return stdout;
}
}