blob: 16ae824c6828a36ad7c6654e717e4ad70831f0df [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 IBM Corporation 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:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.managedbuilder.core.makegen;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedBuildInfo;
import org.eclipse.cdt.managedbuilder.core.IManagedOutputNameProvider;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.makegen.IManagedDependencyGenerator;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.photran.internal.cdtinterface.core.FortranLanguage;
/**
* This class implements the Dependency Manager and Output Name Provider interfaces
* @author Unknown
* @author Timofey Yuvashev 2009
* @author Jeff Overbey -- files were not being closed (Bug 334796)
*/
public class DefaultFortranDependencyCalculator implements IManagedDependencyGenerator,
IManagedOutputNameProvider
{
public static final String MODULE_EXTENSION = "o"; //$NON-NLS-1$
/*
* Return a list of the names of all modules used by a file
*/
private String[] findUsedModuleNames(File file) {
ArrayList names = new ArrayList();
InputStream in = null;
Reader r = null;
try {
/*
InputStream in = new BufferedInputStream(new FileInputStream(file));
ILexer lexer = FortranProcessor.createLexerFor(in, file.getName());
for (Token thisToken = lexer.yylex(), lastToken = null;
thisToken.getTerminal() != Terminal.END_OF_INPUT;
lastToken = thisToken, thisToken = lexer.yylex())
{
if (lastToken != null
&& lastToken.getTerminal() == Terminal.T_USE
&& thisToken.getTerminal() == Terminal.T_IDENT)
{
names.add(thisToken.getText());
}
}
*/
in = new BufferedInputStream(new FileInputStream(file));
r = new BufferedReader(new InputStreamReader(in));
StreamTokenizer st = new StreamTokenizer(r);
st.commentChar('!');
st.eolIsSignificant(false);
st.slashSlashComments(false);
st.slashStarComments(false);
st.wordChars('_', '_');
int token;
while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) {
if (st.ttype == StreamTokenizer.TT_WORD) {
if (st.sval.equalsIgnoreCase("use")) {
token = st.nextToken();
if (st.ttype == StreamTokenizer.TT_WORD) {
names.add(st.sval);
} else {
st.pushBack();
}
}
/**
* This should be moved to separate include file list
*/
/*
else if (st.sval.equalsIgnoreCase("include")) {
token = st.nextToken();
if (st.ttype == '\'' || st.ttype == '"') {
names.add(st.sval);
} else {
st.pushBack();
}
}
*/
}
}
}
catch (Exception e) {
return new String[0];
}
finally {
try {
if (r != null)
r.close();
else if (in != null)
in.close();
}
catch (IOException e) {
}
}
return (String[]) names.toArray(new String[names.size()]);
}
/*
* Return a list of the names of all modules defined in a file
*/
private String[] findModuleNames(File file) {
ArrayList names = new ArrayList();
InputStream in = null;
Reader r = null;
try {
/*
InputStream in = new BufferedInputStream(new FileInputStream(file));
ILexer lexer = FortranProcessor.createLexerFor(in, file.getName());
for (Token thisToken = lexer.yylex(), lastToken = null, tokenBeforeLast = null;
thisToken.getTerminal() != Terminal.END_OF_INPUT;
tokenBeforeLast = lastToken, lastToken = thisToken, thisToken = lexer.yylex())
{
if (lastToken != null
&& lastToken.getTerminal() == Terminal.T_MODULE
&& thisToken.getTerminal() == Terminal.T_IDENT)
{
if (tokenBeforeLast != null && tokenBeforeLast.getTerminal() == Terminal.T_END) {
continue;
}
names.add(thisToken.getText());
}
}
*/
in = new BufferedInputStream(new FileInputStream(file));
r = new BufferedReader(new InputStreamReader(in));
StreamTokenizer st = new StreamTokenizer(r);
st.commentChar('!');
st.eolIsSignificant(false);
st.slashSlashComments(false);
st.slashStarComments(false);
st.wordChars('_', '_');
int token;
while ((token = st.nextToken()) != StreamTokenizer.TT_EOF) {
if (st.ttype == StreamTokenizer.TT_WORD) {
if (st.sval.equalsIgnoreCase("module")) {
token = st.nextToken();
if (st.ttype == StreamTokenizer.TT_WORD) {
names.add(st.sval);
} else {
st.pushBack();
}
}
}
}
}
catch (Exception e) {
return new String[0];
}
finally {
try {
if (r != null)
r.close();
else if (in != null)
in.close();
}
catch (IOException e) {
}
}
return (String[]) names.toArray(new String[names.size()]);
}
/*
* Returns true if the resource is a Fortran source file
*/
private boolean isFortranFile(IProject project, File file, Collection fortranContentTypes) {
try {
IContentType ct = CCorePlugin.getContentType(project, file.getCanonicalPath());
if (ct != null) {
return fortranContentTypes.contains(ct.toString());
}
} catch (Exception e) {
}
return false;
}
/*
* Given a set of the module names used by a source file, and a set of resources to search, determine
* if any of the source files implements the module names.
*/
private IResource[] FindModulesInResources(IProject project,
Collection contentTypes,
IResource resource,
IResource[] resourcesToSearch,
String topBuildDir,
String[] usedNames)
{
IResource[] res = null;
try {
res = project.members();
} catch (CoreException e1) {
throw new Error("No files found in the given project");
}
ArrayList modRes = new ArrayList();
for (int ir = 0; ir < resourcesToSearch.length; ir++) {
if (resourcesToSearch[ir].equals(resource)) continue;
if (resourcesToSearch[ir].getType() == IResource.FILE) {
File projectFile = resourcesToSearch[ir].getLocation().toFile();
if (!isFortranFile(project, projectFile, contentTypes)) continue;
String[] modules = findModuleNames(projectFile);
if (modules != null) {
for (int iu = 0; iu < usedNames.length; iu++) {
boolean foundDependency = false;
for (int im = 0; im < modules.length; im++) {
if (usedNames[iu].equalsIgnoreCase(modules[im])) {
// Get the path to the module file that will be created by the build. By default, ifort appears
// to generate .mod files in the directory from which the compiler is run. For MBS, this
// is the top-level build directory.
// TODO: Support the /module:path option and use that in determining the path of the module file
IPath modName = getModulePath(topBuildDir, modules[im], res, project);
modRes.add(project.getFile(modName));
foundDependency = true;
break;
}
}
if (foundDependency) break;
}
}
} else if (resourcesToSearch[ir].getType() == IResource.FOLDER) {
if (!((IFolder)resourcesToSearch[ir]).isDerived()) {
try {
IResource[] modFound = FindModulesInResources(project, contentTypes, resource, ((IFolder)resourcesToSearch[ir]).members(),
topBuildDir, usedNames);
if (modFound != null) {
for (int i=0; i<modFound.length; i++) {
modRes.add(modFound[i]);
}
}
} catch(Exception e) {}
}
}
}
return (IResource[]) modRes.toArray(new IResource[modRes.size()]);
}
/*
* Finds the relative path to the file whose name matches of the given module. If no such file
* is found, the "Debug" folder is erased and an error is thrown.
*
* @topBuildDir - name of directory (possibly relative path) in which make/object files will be created
* @moduleName - name of the module that we are comparing files against
* @project - project we are currently compiling. Used to remove the Debug directory in case of failure
*/
private IPath getModulePath(String topBuildDir, String moduleName, IResource[] resources, IProject project)
{
String fileNameContainingModule;
fileNameContainingModule = getFileNameContainingModule(moduleName, resources, topBuildDir);
//If we can't find any files with that module, remove Debug folder
if(fileNameContainingModule == null || fileNameContainingModule == "")
{
removeDir(new File(project.getLocation().toString() + Path.SEPARATOR + topBuildDir));
try
{
project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
}
catch (CoreException e)
{
throw new Error("Could not update the project");
}
throw new Error("Could not find a file to match the module name: "+ moduleName);
}
IPath p = Path.fromOSString("./"+topBuildDir + Path.SEPARATOR + fileNameContainingModule + "." + MODULE_EXTENSION);
return p;
}
/*
* This method operates under that assumption that
* 1. There will be no 2 files that are named the same, even if they are in separate folder
* 2. There is only 1 module per file
* 3. There are no files that differ in their names only by upper or lower case letters (i.e. file1.f90 and fILe1.f90)
*
* This method finds the relative path to the filename that matches the name of the given module. It
* searches the list of given @resources to compare the names of contained files. It returns a string which
* is the relative path to the file supposedly containing the given module, with that file's extension chopped off.
*
* @moduleName - name of the module that we are comparing files against
* @resources - list of resources that we look through (could be files and folders)
*/
private String getFileNameContainingModule(String moduleName, IResource[] resources, String buildDirName)
{
ArrayList possibleMatchingFiles = new ArrayList();
if(resources == null || resources.length < 1 ||
moduleName == null || moduleName == "")
{
return null;
}
for(int i = 0; i < resources.length; i++)
{
if(resources[i] instanceof IFile)
{
IFile f = (IFile)resources[i];
//Gets rid of the file extension
String fileName = f.getName().replaceFirst("\\..+", "");
//If a file name matches a module name exactly -- return the relative path to that file
if(fileName == moduleName)
return f.getProjectRelativePath().toString().replaceFirst("\\..+", "");
//Otherwise, check if the two names have different cases
else if(fileName.equalsIgnoreCase(moduleName))
{
//And if they do, keep it
possibleMatchingFiles.add(f.getProjectRelativePath().toString().replaceFirst("\\..+", ""));
}
}
//If its a folder, recurse, but don't look in other build folders (Bug 326333)
else if(resources[i] instanceof IContainer && !resources[i].isDerived())
{
IContainer folder = (IContainer)resources[i];
IResource[] subResource = null;
//Skip build folder, because it contains files with the same
// names as the one we are looking for (created .o files)
if(folder.getName().equalsIgnoreCase(buildDirName))
continue;
try
{
subResource = folder.members();
}
catch (CoreException e)
{
throw new Error("Could not open a container to explore its files");
}
String name = getFileNameContainingModule(moduleName, subResource, buildDirName);
if(name != null)
return name;
}
}
if(possibleMatchingFiles.size() == 1)
return (String) possibleMatchingFiles.get(0);
return null;
}
private void removeDir(File f)
{
if(f != null && f.exists())
{
File[] files = f.listFiles();
for(int i = 0; i < files.length; i++)
{
if(files[i].isDirectory())
removeDir(files[i]);
else
files[i].delete();
}
}
f.delete();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderDependencyCalculator#findDependencies(org.eclipse.core.resources.IResource)
*/
public IResource[] findDependencies(IResource resource, IProject project) {
ArrayList dependencies = new ArrayList();
Collection fortranContentTypes = new FortranLanguage().getRegisteredContentTypeIds();
// TODO: This method should be passed the ITool and the relative path of the top build directory
// For now we'll figure this out from the project.
IManagedBuildInfo mngInfo = ManagedBuildManager.getBuildInfo(project);
IConfiguration config = mngInfo.getDefaultConfiguration();
File file = resource.getLocation().toFile();
try {
if (!isFortranFile(project, file, fortranContentTypes)) {
return new IResource[0];
}
// add dependency on self
dependencies.add(resource);
// Get the names of the modules USE'd by the source file
String[] usedNames = findUsedModuleNames(file);
if (usedNames.length != 0) {
// Search the project files for a Fortran source that creates the module. If we find one, then compiling this
// source file is dependent upon first compiling the found source file.
IResource[] resources = project.members();
IResource[] modRes = FindModulesInResources(project, fortranContentTypes, resource, resources, config.getName(), usedNames);
if (modRes != null) {
for (int i=0; i<modRes.length; i++) {
dependencies.add(modRes[i]);
}
}
}
}
catch (Exception e)
{
return new IResource[0];
}
return (IResource[]) dependencies.toArray(new IResource[dependencies.size()]);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderDependencyCalculator#getCalculatorType()
*/
public int getCalculatorType() {
return TYPE_EXTERNAL;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.makegen.IManagedBuilderDependencyCalculator#getDependencyCommand()
*/
public String getDependencyCommand(IResource resource, IManagedBuildInfo info) {
/*
* The type of this IManagedDependencyGenerator is TYPE_EXTERNAL,
* so implement findDependencies() rather than getDependencyCommand().
* */
return null;
}
/*
* (non-Javadoc)
* @see org.eclipse.cdt.managedbuilder.core.IManagedOutputNameProvider#getOutputNames(org.eclipse.cdt.managedbuilder.core.ITool, org.eclipse.core.runtime.IPath[])
*/
public IPath[] getOutputNames(ITool tool, IPath[] primaryInputNames) {
// TODO: This method should be passed the relative path of the top build directory?
ArrayList outs = new ArrayList();
if (primaryInputNames.length > 0) {
// Get the names of modules created by this source file
String[] modules = findModuleNames(primaryInputNames[0].toFile());
// Add any generated modules
if (modules != null) {
for (int i = 0; i < modules.length; i++) {
// Return the path to the module file that will be created by the build. By default, ifort appears
// to generate .mod files in the directory from which the compiler is run. For MBS, this
// is the top-level build directory.
// TODO: Support the /module:path option and use that in determining the path of the module file
// TODO: The nameProvider documentation should note that the returned path is relative to the top-level
// build directory. HOWEVER, if only a file name is returned, MBS will automatically add on the
// directory path relative to the top-level build directory. The relative path comes from the source
// file location. In order to specify that this output file is always in the top-level build
// directory, regardless of the source file directory structure, return "./path".
IPath modName = Path.fromOSString("." + Path.SEPARATOR + modules[i] + "." + MODULE_EXTENSION);
outs.add(modName);
}
}
}
return (IPath[]) outs.toArray(new IPath[outs.size()]);
}
}