package com.mobilesorcery.sdk.molint.rules; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.model.ICModelMarker; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import com.mobilesorcery.sdk.core.IBuildVariant; import com.mobilesorcery.sdk.core.MoSyncBuilder; import com.mobilesorcery.sdk.core.MoSyncProject; import com.mobilesorcery.sdk.core.templates.ProjectTemplate; import com.mobilesorcery.sdk.core.templates.TemplateManager; import com.mobilesorcery.sdk.molint.CPPASTMolintRule; import com.mobilesorcery.sdk.molint.MolintPlugin; public class MAHeaderRule extends CPPASTMolintRule { private static final String RULE_ID = "maheader"; public MAHeaderRule() { super(RULE_ID, "MAHeaders.h Location"); } public List<IMarker> analyze(IProgressMonitor monitor, MoSyncProject mosyncProject, IBuildVariant variant) throws CoreException { // Our analysis only applies to C/C++ project that has resources String templateId = mosyncProject .getProperty(MoSyncProject.TEMPLATE_ID); ProjectTemplate projectTemplate = templateId == null ? null : TemplateManager.getDefault().getProjectTemplate(templateId); boolean isCppTemplate = projectTemplate != null && ProjectTemplate.DEFAULT_TYPE.equals(projectTemplate .getType()); boolean usesResourcesDir = MoSyncBuilder .getResourcesDirectory(mosyncProject.getWrappedProject()) != null; final boolean[] hasResourceFiles = new boolean[] { usesResourcesDir }; mosyncProject.getWrappedProject().accept(new IResourceVisitor() { @Override public boolean visit(IResource resource) throws CoreException { hasResourceFiles[0] |= MoSyncBuilder.isResourceFile(resource) && !MoSyncBuilder.isInOutput(resource.getProject(), resource); // Once we find one, we are done. return !hasResourceFiles[0]; } }); if (isCppTemplate && hasResourceFiles[0]) { return super.analyze(monitor, mosyncProject, variant); } else { return null; } } @Override protected List<IMarker> analyze(MoSyncProject project, IBuildVariant variant, ITranslationUnit tu, IASTTranslationUnit ast) throws CoreException { ArrayList<IMarker> result = new ArrayList<IMarker>(); IASTPreprocessorIncludeStatement[] directives = ast .getIncludeDirectives(); String maheaderError = null; IASTFileLocation errorLocation = null; for (int i = 0; i < directives.length && maheaderError == null; i++) { IASTPreprocessorIncludeStatement directive = directives[i]; String includePath = directive.getName().toString(); boolean foundMAHeaderInclude = includePath.toLowerCase().contains( "maheaders"); // Why not use CDTs resolver? It uses heuristics and may resolve the // file to // the wrong location. IPath includeFile = null; if (foundMAHeaderInclude) { includeFile = resolveInclude(new Path(includePath), project, variant, directive.isSystemInclude(), new Path(directive.getContainingFilename())); } //IFile[] includeFiles = ResourcesPlugin.getWorkspace().getRoot() // .findFilesForLocationURI(new File(includePath).toURI()); //for (IFile includeFile : includeFiles) { // if (foundMAHeaderInclude // && includeFile.getProject() == project // .getWrappedProject()) { IPath expectedLocation = MoSyncBuilder.getOutputPath( project.getWrappedProject(), variant); if (includeFile != null && !includeFile.removeLastSegments(1) .equals(expectedLocation)) { maheaderError = "MAHeaders.h should be auto-generated, found MAHeaders.h inclusion in source path."; IASTNodeLocation[] location = directive .getNodeLocations(); if (location.length > 0 && location[0] instanceof IASTFileLocation) { errorLocation = (IASTFileLocation) location[0]; } // } //} } } if (maheaderError != null) { IResource file = tu.getResource(); IMarker marker = file .createMarker(ICModelMarker.C_MODEL_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, maheaderError); marker.setAttribute(IMarker.SEVERITY, getSeverity(IMarker.SEVERITY_ERROR)); if (errorLocation != null) { marker.setAttribute(IMarker.LINE_NUMBER, errorLocation.getStartingLineNumber()); marker.setAttribute(IMarker.CHAR_START, errorLocation.getNodeOffset()); marker.setAttribute( IMarker.CHAR_END, errorLocation.getNodeOffset() + errorLocation.getNodeLength()); } result.add(marker); } return result; } private IPath resolveInclude(IPath includePath, MoSyncProject project, IBuildVariant variant, boolean systemInclude, IPath baseLocation) throws CoreException { try { List<IPath> paths = new ArrayList<IPath>(Arrays.asList(MoSyncBuilder .getBaseIncludePaths(project, variant))); // Note: includePaths does NOT include the output path, we add it // ourselves. paths.add(MoSyncBuilder.getOutputPath(project.getWrappedProject(), variant)); if (!systemInclude) { // First add the . directory paths.add(0, baseLocation.removeLastSegments(1)); // Then check the absolute if (includePath.toFile().exists()) { return includePath; } } for (IPath path : paths) { IPath potentialPath = path.append(includePath); if (potentialPath.toFile().exists()) { return potentialPath; } } return null; } catch (Exception e) { throw new CoreException(new Status(IStatus.ERROR, MolintPlugin.PLUGIN_ID, "Could not resolve includes", e)); } } }