/*==========================================================================*\
| $Id: StaticLibraryManager.java,v 1.5 2009/09/21 14:16:50 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2009 Virginia Tech
|
| This file is part of Web-CAT Eclipse Plugins.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package net.sf.webcat.eclipse.cxxtest.bfd;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import net.sf.webcat.eclipse.cxxtest.CxxTestPlugin;
import net.sf.webcat.eclipse.cxxtest.bfd.i18n.Messages;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager;
import org.eclipse.cdt.managedbuilder.gnu.cygwin.GnuCygwinConfigurationEnvironmentSupplier;
import org.eclipse.cdt.utils.spawner.ProcessFactory;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
//------------------------------------------------------------------------
/**
* TODO: real description
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @author latest changes by: $Author: aallowat $
* @version $Revision: 1.5 $ $Date: 2009/09/21 14:16:50 $
*/
public class StaticLibraryManager
{
private StaticLibraryManager()
{
isWindows = System.getProperty("os.name").toLowerCase().startsWith( //$NON-NLS-1$
"windows "); //$NON-NLS-1$
}
public static StaticLibraryManager getInstance()
{
if (instance == null)
{
instance = new StaticLibraryManager();
}
return instance;
}
public String getMissingLibraryString()
{
ArrayList<String> list = new ArrayList<String>();
if (!hasBfd)
{
list.add("libbfd"); //$NON-NLS-1$
}
if (!hasIntl)
{
list.add("libintl"); //$NON-NLS-1$
}
if (!hasIberty)
{
list.add("libiberty"); //$NON-NLS-1$
}
if (list.isEmpty())
{
return null;
}
StringBuffer buffer = new StringBuffer();
buffer.append(Messages.StaticLibraryManager_MissingLibrariesMsgStart);
buffer.append(list.get(0));
for (int i = 1; i < list.size(); i++)
{
buffer.append(", "); //$NON-NLS-1$
buffer.append(list.get(i));
}
return buffer.toString();
}
public void checkForDependencies(IProgressMonitor monitor)
{
monitor.beginTask(Messages.StaticLibraryManager_CheckingLibraryReqs, 6);
// First make an attempt to just build with bfd. We might not need to
// consider any other dependencies.
monitor.subTask(Messages.StaticLibraryManager_LookingForBfd);
hasBfd = tryToCompile("check-libbfd.c", "bfd"); //$NON-NLS-1$
monitor.worked(1);
if (hasBfd)
{
hasIntl = true;
needsLinkToIntl = false;
hasIberty = true;
needsLinkToIberty = false;
monitor.done();
return;
}
// Check for libintl functions, first without explicitly linking
// (in case they're part of glibc), and then by linking directly.
monitor.subTask(Messages.StaticLibraryManager_LookingForIntlBuiltIn);
boolean hasIntlBuiltIn = tryToCompile("check-libintl.c"); //$NON-NLS-1$
monitor.worked(1);
if (!hasIntlBuiltIn)
{
monitor.subTask(Messages.StaticLibraryManager_LookingForIntlSeparate);
hasIntl = tryToCompile("check-libintl.c", "intl"); //$NON-NLS-1$ //$NON-NLS-2$
needsLinkToIntl = true;
}
else
{
hasIntl = true;
needsLinkToIntl = false;
}
monitor.worked(1);
// Check for libiberty functions, first without explicitly linking
// (in case they're part of glibc), and then by linking directly.
monitor.subTask(Messages.StaticLibraryManager_LookingForIbertyBuiltIn);
boolean hasIbertyBuiltIn = tryToCompile("check-libiberty.c"); //$NON-NLS-1$
monitor.worked(1);
if (!hasIbertyBuiltIn)
{
monitor.subTask(Messages.StaticLibraryManager_LookingForIbertySeparate);
hasIberty = tryToCompile("check-libiberty.c", "iberty"); //$NON-NLS-1$ //$NON-NLS-2$
needsLinkToIberty = true;
}
else
{
hasIberty = true;
needsLinkToIberty = false;
}
monitor.worked(1);
// Now use what we know to try search for libbfd successfully. For
// this to be successful we need to have support for several libintl
// and libiberty functions, so use the information collected above to
// link to them explicitly if necessary.
monitor.subTask(Messages.StaticLibraryManager_LookingForBfd);
hasBfd = tryToCompile("check-libbfd.c", librariesNeededForBfd()); //$NON-NLS-1$
monitor.worked(1);
monitor.done();
}
private String[] librariesNeededForBfd()
{
ArrayList<String> libs = new ArrayList<String>();
libs.add("bfd"); //$NON-NLS-1$
if (shouldAddIntlToBuild())
{
libs.add("intl"); //$NON-NLS-1$
}
if (shouldAddIbertyToBuild())
{
libs.add("iberty"); //$NON-NLS-1$
}
return libs.toArray(new String[libs.size()]);
}
private String[] calculateEnvironment()
{
TreeMap<String, String> envMap;
if (isWindows)
{
envMap = new TreeMap<String, String>(new Comparator<String>() {
public int compare(String lhs, String rhs)
{
return lhs.compareToIgnoreCase(rhs);
}
});
}
else
{
envMap = new TreeMap<String, String>();
}
IEnvironmentVariableManager mngr =
CCorePlugin.getDefault().getBuildEnvironmentManager();
IEnvironmentVariable[] vars = mngr.getVariables(null, true);
if (vars != null)
{
for(int i = 0; i < vars.length; i++)
{
envMap.put(vars[i].getName(), vars[i].getValue());
}
}
GnuCygwinConfigurationEnvironmentSupplier gnu =
new GnuCygwinConfigurationEnvironmentSupplier();
vars = gnu.getVariables(null, null);
if (vars != null)
{
for(int i = 0; i < vars.length; i++)
{
String oldValue = envMap.get(vars[i].getName());
if (oldValue != null)
{
oldValue = vars[i].getValue() + vars[i].getDelimiter() +
oldValue;
}
else
{
oldValue = vars[i].getValue();
}
envMap.put(vars[i].getName(), oldValue);
}
}
List<String> strings = new ArrayList<String>(envMap.size());
for (Map.Entry<String, String> entry : envMap.entrySet())
{
strings.add(entry.getKey() + "=" + entry.getValue()); //$NON-NLS-1$
}
return (String[]) strings.toArray(new String[strings.size()]);
}
private boolean tryToCompile(String sourceFile, String... linkLibraries)
{
String[] envp = calculateEnvironment();
sourceFile = getLibraryCheckPath(sourceFile);
File tempOut = null;
try
{
tempOut = File.createTempFile("libchkexe", null); //$NON-NLS-1$
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
ArrayList<String> argList = new ArrayList<String>();
argList.add("sh"); //$NON-NLS-1$
argList.add("-c"); //$NON-NLS-1$
StringBuffer cmdLine = new StringBuffer();
cmdLine.append("gcc -o "); //$NON-NLS-1$
if (isWindows)
{
cmdLine.append('"');
cmdLine.append(tempOut.getAbsolutePath().replace(
File.separatorChar, '/'));
cmdLine.append('"');
cmdLine.append(" "); //$NON-NLS-1$
cmdLine.append('"');
cmdLine.append(sourceFile.replace(File.separatorChar, '/'));
cmdLine.append('"');
}
else
{
cmdLine.append('"');
cmdLine.append(tempOut.getAbsolutePath());
cmdLine.append('"');
cmdLine.append(" "); //$NON-NLS-1$
cmdLine.append('"');
cmdLine.append(sourceFile);
cmdLine.append('"');
}
if (linkLibraries != null)
{
for (String lib : linkLibraries)
{
cmdLine.append(" -l"); //$NON-NLS-1$
cmdLine.append(lib);
}
}
argList.add(cmdLine.toString());
String[] args = argList.toArray(new String[argList.size()]);
boolean success = false;
Process proc;
try
{
// System.out.println(Arrays.toString(args));
proc = ProcessFactory.getFactory().exec(args, envp);
ProcessClosure closure = new ProcessClosure(proc);
// new NonClosingOutputStreamWrapper(System.out),
// new NonClosingOutputStreamWrapper(System.out));
closure.runBlocking();
success = (proc.exitValue() == 0);
}
catch (IOException e)
{
e.printStackTrace();
}
tempOut.delete();
return success;
}
private String getLibraryCheckPath(String file)
{
String path = null;
try
{
URL entry = FileLocator.find(
CxxTestPlugin.getDefault().getBundle(),
new Path("/library-checks/" + file), null); //$NON-NLS-1$
URL url = FileLocator.resolve(entry);
path = url.getFile();
// This special check is somewhat shady, but it looks like it's
// the only way to handle a Windows path properly, since Eclipse
// returns a string like "/C:/folder/...".
if(path.charAt(2) == ':')
path = path.substring(1);
path = new Path(path).toOSString();
if(path.charAt(path.length() - 1) == File.separatorChar)
path = path.substring(0, path.length() - 1);
}
catch(IOException e)
{
e.printStackTrace();
}
return path;
}
public boolean hasBfd()
{
return hasBfd;
}
public boolean shouldAddIntlToBuild()
{
return needsLinkToIntl && hasIntl;
}
public boolean shouldAddIbertyToBuild()
{
return needsLinkToIberty && hasIberty;
}
private boolean isWindows;
private boolean hasBfd;
private boolean hasIntl;
private boolean needsLinkToIntl;
private boolean hasIberty;
private boolean needsLinkToIberty;
private static StaticLibraryManager instance;
}