package com.redhat.ceylon.eclipse.core.builder; import static com.redhat.ceylon.cmr.ceylon.CeylonUtils.repoManager; import static com.redhat.ceylon.compiler.java.util.Util.getModuleArchiveName; import static com.redhat.ceylon.compiler.java.util.Util.getModulePath; import static com.redhat.ceylon.compiler.java.util.Util.getSourceArchiveName; import static com.redhat.ceylon.eclipse.core.classpath.CeylonClasspathUtil.getCeylonClasspathContainers; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C; import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.PLUGIN_ID; import static com.redhat.ceylon.eclipse.util.CeylonHelper.list; import static com.redhat.ceylon.eclipse.util.CeylonHelper.td; import static com.redhat.ceylon.eclipse.util.CeylonHelper.toJavaStringList; import static com.redhat.ceylon.eclipse.util.InteropUtils.toJavaString; import static com.redhat.ceylon.model.typechecker.model.Module.LANGUAGE_MODULE_NAME; import static org.eclipse.core.resources.IResource.DEPTH_INFINITE; import static org.eclipse.core.resources.IResource.DEPTH_ZERO; import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.antlr.runtime.CommonToken; import org.eclipse.core.internal.events.NotificationManager; import org.eclipse.core.resources.IBuildConfiguration; import org.eclipse.core.resources.IBuildContext; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.ProgressMonitorWrapper; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsoleManager; import org.eclipse.ui.console.MessageConsole; import org.eclipse.ui.console.MessageConsoleStream; import org.eclipse.ui.editors.text.TextFileDocumentProvider; import com.redhat.ceylon.cmr.api.ArtifactContext; import com.redhat.ceylon.cmr.api.ArtifactCreator; import com.redhat.ceylon.cmr.api.RepositoryManager; import com.redhat.ceylon.cmr.ceylon.CeylonUtils; import com.redhat.ceylon.cmr.impl.ShaSigner; import com.redhat.ceylon.common.Backend; import com.redhat.ceylon.common.Backends; import com.redhat.ceylon.common.Constants; import com.redhat.ceylon.common.config.CeylonConfig; import com.redhat.ceylon.common.config.DefaultToolOptions; import com.redhat.ceylon.common.log.Logger; import com.redhat.ceylon.compiler.CeylonCompileTool; import com.redhat.ceylon.compiler.java.codegen.CeylonCompilationUnit; import com.redhat.ceylon.compiler.java.codegen.CeylonFileObject; import com.redhat.ceylon.compiler.java.codegen.Naming; import com.redhat.ceylon.compiler.java.loader.ModelLoaderFactory; import com.redhat.ceylon.compiler.java.loader.TypeFactory; import com.redhat.ceylon.compiler.java.loader.UnknownTypeCollector; import com.redhat.ceylon.compiler.java.tools.CeylonLog; import com.redhat.ceylon.compiler.java.tools.CeyloncFileManager; import com.redhat.ceylon.compiler.java.tools.CeyloncTaskImpl; import com.redhat.ceylon.compiler.java.tools.JarEntryFileObject; import com.redhat.ceylon.compiler.java.tools.LanguageCompiler; import com.redhat.ceylon.compiler.java.util.RepositoryLister; import com.redhat.ceylon.compiler.js.JsCompiler; import com.redhat.ceylon.compiler.js.util.Options; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleSourceMapper; import com.redhat.ceylon.compiler.typechecker.analyzer.Warning; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnits; import com.redhat.ceylon.compiler.typechecker.io.VirtualFile; import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer; import com.redhat.ceylon.compiler.typechecker.tree.AnalysisMessage; import com.redhat.ceylon.compiler.typechecker.tree.Message; import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit; import com.redhat.ceylon.compiler.typechecker.tree.UnexpectedError; import com.redhat.ceylon.compiler.typechecker.util.WarningSuppressionVisitor; import com.redhat.ceylon.eclipse.code.editor.CeylonTaskUtil; import com.redhat.ceylon.eclipse.core.classpath.CeylonLanguageModuleContainer; import com.redhat.ceylon.eclipse.core.classpath.CeylonProjectModulesContainer; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; import com.redhat.ceylon.eclipse.ui.CeylonResources; import com.redhat.ceylon.eclipse.util.CeylonHelper; import com.redhat.ceylon.eclipse.util.EclipseLogger; import com.redhat.ceylon.ide.common.model.BaseCeylonProject; import com.redhat.ceylon.ide.common.model.BaseIdeModelLoader; import com.redhat.ceylon.ide.common.model.BaseIdeModule; import com.redhat.ceylon.ide.common.model.BaseIdeModuleManager; import com.redhat.ceylon.ide.common.model.BaseIdeModuleSourceMapper; import com.redhat.ceylon.ide.common.model.CeylonBinaryUnit; import com.redhat.ceylon.ide.common.model.CeylonIdeConfig; import com.redhat.ceylon.ide.common.model.CeylonProject; import com.redhat.ceylon.ide.common.model.CeylonProjectConfig; import com.redhat.ceylon.ide.common.model.CeylonUnit; import com.redhat.ceylon.ide.common.model.IJavaModelAware; import com.redhat.ceylon.ide.common.model.IResourceAware; import com.redhat.ceylon.ide.common.model.IdeModelLoader; import com.redhat.ceylon.ide.common.model.JavaCompilationUnit; import com.redhat.ceylon.ide.common.model.JavaUnit; import com.redhat.ceylon.ide.common.model.ModuleDependencies; import com.redhat.ceylon.ide.common.model.ProjectSourceFile; import com.redhat.ceylon.ide.common.model.ProjectState; import com.redhat.ceylon.ide.common.model.SourceFile; import com.redhat.ceylon.ide.common.model.delta.CompilationUnitDelta; import com.redhat.ceylon.ide.common.model.parsing.ProjectSourceParser; import com.redhat.ceylon.ide.common.typechecker.ExternalPhasedUnit; import com.redhat.ceylon.ide.common.typechecker.ProjectPhasedUnit; import com.redhat.ceylon.ide.common.util.CarUtils; import com.redhat.ceylon.ide.common.util.ProgressMonitor; import com.redhat.ceylon.ide.common.util.ProgressMonitor$impl; import com.redhat.ceylon.ide.common.util.ProgressMonitorChild; import com.redhat.ceylon.ide.common.vfs.FileVirtualFile; import com.redhat.ceylon.ide.common.vfs.FolderVirtualFile; import com.redhat.ceylon.ide.common.vfs.ResourceVirtualFile; import com.redhat.ceylon.javax.tools.DiagnosticListener; import com.redhat.ceylon.javax.tools.FileObject; import com.redhat.ceylon.javax.tools.JavaFileObject; import com.redhat.ceylon.langtools.source.tree.CompilationUnitTree; import com.redhat.ceylon.langtools.source.util.TaskEvent; import com.redhat.ceylon.langtools.source.util.TaskEvent.Kind; import com.redhat.ceylon.langtools.source.util.TaskListener; import com.redhat.ceylon.langtools.tools.javac.file.RegularFileObject; import com.redhat.ceylon.langtools.tools.javac.file.RelativePath.RelativeFile; import com.redhat.ceylon.langtools.tools.javac.tree.JCTree; import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCClassDecl; import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCCompilationUnit; import com.redhat.ceylon.langtools.tools.javac.util.Context; import com.redhat.ceylon.model.loader.AbstractModelLoader; import com.redhat.ceylon.model.typechecker.context.TypeCache; import com.redhat.ceylon.model.typechecker.model.Cancellable; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Modules; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.model.typechecker.model.Unit; import com.redhat.ceylon.model.typechecker.util.ModuleManager; import ceylon.interop.java.JavaIterable; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; /** * A builder may be activated on a file containing ceylon code every time it has * changed (when "Build automatically" is on), or when the programmer chooses to * "Build" a project. * * TODO This default implementation was generated from a template, it needs to * be completed manually. */ public class CeylonBuilder extends IncrementalProjectBuilder { public static final String CEYLON_CLASSES_FOLDER_NAME = ".exploded"; /** * Extension ID of the Ceylon builder, which matches the ID in the * corresponding extension definition in plugin.xml. */ public static final String BUILDER_ID = PLUGIN_ID + ".ceylonBuilder"; /** * A marker ID that identifies problems */ public static final String PROBLEM_MARKER_ID = PLUGIN_ID + ".ceylonProblem"; /** * A marker ID that identifies module dependency problems */ public static final String MODULE_DEPENDENCY_PROBLEM_MARKER_ID = PLUGIN_ID + ".ceylonModuleDependencyProblem"; /** * A marker ID that identifies character encoding problems */ public static final String CHARSET_PROBLEM_MARKER_ID = PLUGIN_ID + ".ceylonCharsetProblem"; /** * A marker ID that identifies character encoding problems */ public static final String CEYLON_CONFIG_NOT_IN_SYNC_MARKER = PLUGIN_ID + ".ceylonConfigProblem"; /** * A marker ID that identifies Invalid Overrides XML file problems */ public static final String CEYLON_INVALID_OVERRIDES_MARKER = PLUGIN_ID + ".invalidOverridesProblem"; /** * A marker ID that identifies tasks */ public static final String TASK_MARKER_ID = PLUGIN_ID + ".ceylonTask"; public static final String SOURCE = "Ceylon"; static { TypeCache.setEnabledByDefault(false); } public static <T> T doWithCeylonModelCaching(final Callable<T> action) throws CoreException { Boolean was = TypeCache.setEnabled(true); try { return action.call(); } catch(CoreException ce) { throw ce; } catch(Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } finally { TypeCache.setEnabled(was); } } private static ReentrantReadWriteLock getProjectSourceModelLock(IProject project) { BaseCeylonProject ceylonProject = modelJ2C().ceylonModel().getProject(project); return ceylonProject != null ? ceylonProject.getSourceModelLock() : null; } /* * Allows synchronizing some code that touches the source-related * Ceylon model, by setting up the typechecker, creating or * typechecking PhasedUnits, etc ... * * It's based on a ReentrantReadWriteLock. * * To avoid deadlock, it always takes a time limit, * after which the it stops waiting for the source * model availability and throw an OperationCanceled Exception * */ public static <T> T doWithSourceModel( IProject project, boolean readonly, final long waitForModelInSeconds, Callable<T> action) { try { if (project== null) { return action.call(); } ReadWriteLock projectLock = getProjectSourceModelLock(project); if (projectLock == null) { return action.call(); } Lock sourceModelLock = readonly ? projectLock.readLock() : projectLock.writeLock(); if (sourceModelLock.tryLock(waitForModelInSeconds, TimeUnit.SECONDS)) { try { return action.call(); } finally { sourceModelLock.unlock(); } } else { throw new OperationCanceledException("The source model " + (readonly ? "read" : "write") + " lock of project " + project + " could not be acquired within " + waitForModelInSeconds+ " seconds"); } } catch(InterruptedException ie) { throw new OperationCanceledException("The thread was interrupted " + "while waiting for the source model " + (readonly ? "read" : "write") + " lock of project " + project); } catch(Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } } private static class BuildFileManager extends CeyloncFileManager { private final IProject project; final boolean explodeModules; private Map<RegularFileObject, Set<String>> inputFilesToGenerate = null; private BuildFileManager(com.redhat.ceylon.langtools.tools.javac.util.Context context, boolean register, Charset charset, IProject project, Map<RegularFileObject, Set<String>> inputFilesToGenerate) { super(context, register, charset); this.project = project; explodeModules = isExplodeModulesEnabled(project); this.inputFilesToGenerate = inputFilesToGenerate; } public static RegularFileObject getSourceFile(FileObject fileObject) { JavaFileObject sourceJavaFileObject; if (fileObject instanceof JavaFileObject && ((JavaFileObject) fileObject).getKind() == com.redhat.ceylon.javax.tools.JavaFileObject.Kind.SOURCE){ if (fileObject instanceof CeylonFileObject) { sourceJavaFileObject = ((CeylonFileObject) fileObject).getFile(); } else { sourceJavaFileObject = (JavaFileObject) fileObject; } if (sourceJavaFileObject instanceof RegularFileObject) { return ((RegularFileObject) sourceJavaFileObject); } } return null; } @Override protected JavaFileObject getFileForOutput(Location location, final RelativeFile fileName, FileObject sibling) throws IOException { RegularFileObject sourceFile = getSourceFile(sibling); if (sourceFile != null) { Set<String> expectedClasses = inputFilesToGenerate.get(sourceFile); String shortname = fileName.basename(); if (shortname.endsWith(".class")) { shortname = shortname.substring(0, shortname.length() - 6); } if (expectedClasses != null) { expectedClasses.remove(shortname); if (expectedClasses.isEmpty()) { inputFilesToGenerate.remove(sourceFile); } } else { System.out.println("WARNING : com.redhat.ceylon.eclipse.core.builder.CeylonBuilder$BuildFileManager.getFileForOutput().expectedClasses is null for source file " + sourceFile + "\n Is it normal ? it seems getFileForOutput was called several times on the same file during binary generation."); } } JavaFileObject javaFileObject = super.getFileForOutput(location, fileName, sibling); if (explodeModules && javaFileObject instanceof JarEntryFileObject && sibling instanceof CeylonFileObject) { final File ceylonOutputDirectory = getCeylonClassesOutputDirectory(project); final File classFile = fileName.getFile(ceylonOutputDirectory); classFile.getParentFile().mkdirs(); return new ExplodingJavaFileObject(classFile, fileName, javaFileObject); } return javaFileObject; } @Override protected String getCurrentWorkingDir() { return project.getLocation().toFile().getAbsolutePath(); } public void addUngeneratedErrors() { if (inputFilesToGenerate.size() > 0) { try { String markerId = PROBLEM_MARKER_ID + ".backend"; String message = "Some classes are missing from the generated module archives, probably because of an error in the Java backend compilation.\n" + "The detail of missing classes is given in the Information markers."; IMarker marker = project.createMarker(markerId); marker.setAttribute(IMarker.MESSAGE, message); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); } catch (CoreException ce) { ce.printStackTrace(); } } for (RegularFileObject sourceFileNotGenerated : inputFilesToGenerate.keySet()) { IPath absolutePath = new Path(sourceFileNotGenerated.getName()); IFile file = null; for (IFolder sourceDirectory : CeylonBuilder.getSourceFolders(project)) { IPath sourceDirPath = sourceDirectory.getLocation(); if (sourceDirPath.isPrefixOf(absolutePath)) { IResource r = sourceDirectory.findMember(absolutePath.makeRelativeTo(sourceDirPath)); if (r instanceof IFile) { file = (IFile) r; } } } if (file == null) { file = getWorkspace().getRoot() .getFileForLocation(new Path(sourceFileNotGenerated.getName())); } if (file != null) { try { String markerId = PROBLEM_MARKER_ID + ".backend"; String message = "The following classes were not generated by the backend :"; Iterator<String> classes = inputFilesToGenerate.get(sourceFileNotGenerated).iterator(); String line = ""; if (classes.hasNext()) { line += "\n " + classes.next(); } while (classes.hasNext()) { if (line.length() > 70) { message += line; line = "\n "; } else { line += ", "; } line += classes.next(); } if (! line.trim().isEmpty()) { message += line; } IMarker marker = file.createMarker(markerId); marker.setAttribute(IMarker.MESSAGE, message); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); } catch (CoreException ce) { ce.printStackTrace(); } } } } } private static Set<IProject> containersInitialized = new HashSet<IProject>(); public static final String CEYLON_CONSOLE= "Ceylon Build"; //private long startTime; public static boolean isModelTypeChecked(IProject project) { BaseCeylonProject ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return ceylonProject.getTypechecked(); } return false; } public static boolean isModelParsed(IProject project) { BaseCeylonProject ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return ceylonProject.getParsed(); } return false; } public static List<PhasedUnit> getUnits(IProject project) { BaseCeylonProject ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return list(PhasedUnit.class, ceylonProject.getParsedUnits()); } return Collections.emptyList(); } public static List<PhasedUnit> getUnits() { return list(PhasedUnit.class, modelJ2C().ceylonModel().getParsedUnits()); } // public static List<PhasedUnit> getUnits(String[] projects) { // List<PhasedUnit> result = new ArrayList<PhasedUnit>(); // if (projects!=null) { // for (Map.Entry<IProject, TypeChecker> me: typeCheckers.entrySet()) { // for (String pname: projects) { // if (me.getKey().getName().equals(pname)) { // IProject project = me.getKey(); // if (isModelParsed(project)) { // result.addAll(me.getValue().getPhasedUnits().getPhasedUnits()); // } // } // } // } // } // return result; // } public String getBuilderID() { return BUILDER_ID; } public static boolean isCeylon(IFile file) { String ext = file.getFileExtension(); return ext!=null && ext.equals("ceylon"); } public static boolean isJava(IFile file) { return JavaCore.isJavaLikeFileName(file.getName()); } public static boolean isJavascript(IFile file) { String ext = file.getFileExtension(); return ext!=null && ext.equals("js"); } /* * A source file is compilable and located in a Ceylon source folder */ public static boolean isSourceFile(IFile file) { // If the file is not in a ceylon source folder // it's not considered as a source file // even if it is compilable return isCompilable(file) && isInSourceFolder(file); } public static boolean isCompilable(IFile file) { if (isCeylon(file)) { return true; } if (isJava(file) && compileToJava(file.getProject())) { return true; } if (isJavascript(file) && compileToJs(file.getProject())) { return true; } return false; } public static boolean isResourceFile(IFile file) { RootFolderType rootFolderType = getRootFolderType(file); return rootFolderType == RootFolderType.RESOURCE; } @SuppressWarnings("unchecked") public static IdeModelLoader<IProject, IResource, IFolder, IFile, ITypeRoot, IType> getModelLoader(TypeChecker typeChecker) { BaseIdeModuleManager moduleManager = (BaseIdeModuleManager) typeChecker.getPhasedUnits().getModuleManager(); return (IdeModelLoader<IProject, IResource, IFolder, IFile, ITypeRoot, IType>) moduleManager.getModelLoader(); } public static IdeModelLoader<IProject, IResource, IFolder, IFile, ITypeRoot, IType> getProjectModelLoader(IProject project) { TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker == null) { return null; } return getModelLoader(typeChecker); } public static BaseIdeModuleManager getProjectModuleManager(IProject project) { BaseIdeModelLoader modelLoader = getProjectModelLoader(project); if (modelLoader == null) { return null; } return modelLoader.getModuleManager(); } public final static class BooleanHolder { public boolean value; } public static class CeylonBuildHook { protected void startBuild(int kind, @SuppressWarnings("rawtypes") Map args, IProject javaProject, IBuildConfiguration config, IBuildContext context, IProgressMonitor monitor) throws CoreException {} protected void deltasAnalyzed(List<IResourceDelta> currentDeltas, BooleanHolder sourceModified, BooleanHolder mustDoFullBuild, BooleanHolder mustResolveClasspathContainer, boolean mustContinueBuild) {} protected void resolvingClasspathContainer( List<IClasspathContainer> cpContainers) {} protected void setAndRefreshClasspathContainer() {} protected void doFullBuild() {} protected void parseCeylonModel() {} protected void doIncrementalBuild() {} protected void fullTypeCheckDuringIncrementalBuild() {} protected void incrementalBuildChangedSources(Set<IFile> changedSources) {} protected void incrementalBuildSources(Set<IFile> changedSources, List<IFile> filesToRemove, Collection<IFile> sourcesToCompile) {} protected void incrementalBuildResult(List<PhasedUnit> builtPhasedUnits) {} protected void beforeGeneratingBinaries() {} protected void afterGeneratingBinaries() {} protected void scheduleReentrantBuild() {} protected void afterReentrantBuild() {} protected void endBuild() {} }; public static final CeylonBuildHook noOpHook = new CeylonBuildHook(); public static enum RootFolderType { SOURCE, RESOURCE } private static CeylonBuildHook buildHook = new CeylonBuildHook() { List<CeylonBuildHook> contributedHooks = new LinkedList<>(); private synchronized void resetContributedHooks() { contributedHooks.clear(); for (IConfigurationElement confElement : Platform.getExtensionRegistry().getConfigurationElementsFor(CeylonPlugin.PLUGIN_ID + ".ceylonBuildHook")) { try { Object extension = confElement.createExecutableExtension("class"); if (extension instanceof ICeylonBuildHookProvider) { CeylonBuildHook hook = ((ICeylonBuildHookProvider) extension).getHook(); if (hook != null) { contributedHooks.add(hook); } } } catch (CoreException e) { e.printStackTrace(); } } } protected void startBuild(int kind, @SuppressWarnings("rawtypes") Map args, IProject javaProject, IBuildConfiguration config, IBuildContext context, IProgressMonitor monitor) throws CoreException { resetContributedHooks(); for (CeylonBuildHook hook : contributedHooks) { hook.startBuild(kind, args, javaProject, config, context, monitor); } } protected void deltasAnalyzed(List<IResourceDelta> currentDeltas, BooleanHolder sourceModified, BooleanHolder mustDoFullBuild, BooleanHolder mustResolveClasspathContainer, boolean mustContinueBuild) { for (CeylonBuildHook hook : contributedHooks) { hook.deltasAnalyzed(currentDeltas, sourceModified, mustDoFullBuild, mustResolveClasspathContainer, mustContinueBuild); } } protected void resolvingClasspathContainer( List<IClasspathContainer> cpContainers) { for (CeylonBuildHook hook : contributedHooks) { hook.resolvingClasspathContainer(cpContainers); } } protected void setAndRefreshClasspathContainer() { for (CeylonBuildHook hook : contributedHooks) { hook.setAndRefreshClasspathContainer(); } } protected void doFullBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.doFullBuild(); } } protected void parseCeylonModel() { for (CeylonBuildHook hook : contributedHooks) { hook.parseCeylonModel(); } } protected void doIncrementalBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.doIncrementalBuild(); } } protected void fullTypeCheckDuringIncrementalBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.fullTypeCheckDuringIncrementalBuild(); } } protected void incrementalBuildChangedSources(Set<IFile> changedSources) { for (CeylonBuildHook hook : contributedHooks) { hook.incrementalBuildChangedSources(changedSources); } } protected void incrementalBuildSources(Set<IFile> changedSources, List<IFile> filesToRemove, Collection<IFile> sourcesToCompile) { for (CeylonBuildHook hook : contributedHooks) { hook.incrementalBuildSources(changedSources, filesToRemove, sourcesToCompile); } } protected void incrementalBuildResult(List<PhasedUnit> builtPhasedUnits) { for (CeylonBuildHook hook : contributedHooks) { hook.incrementalBuildResult(builtPhasedUnits); } } protected void beforeGeneratingBinaries() { for (CeylonBuildHook hook : contributedHooks) { hook.beforeGeneratingBinaries(); } } protected void afterGeneratingBinaries() { for (CeylonBuildHook hook : contributedHooks) { hook.afterGeneratingBinaries(); } } protected void scheduleReentrantBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.beforeGeneratingBinaries(); } } protected void afterReentrantBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.afterReentrantBuild(); } } protected void endBuild() { for (CeylonBuildHook hook : contributedHooks) { hook.endBuild(); } } }; public static CeylonBuildHook replaceHook(CeylonBuildHook hook){ CeylonBuildHook previousHook = buildHook; buildHook = hook; return previousHook; } private static WeakReference<Job> notificationJobReference = null; private static synchronized Job getNotificationJob() { Job job = null; if (notificationJobReference != null) { job = notificationJobReference.get(); } if (job == null) { for (Job j : Job.getJobManager().find(null)) { if (NotificationManager.class.equals(j.getClass().getEnclosingClass())) { job = j; notificationJobReference = new WeakReference<Job>(job); break; } } } return job; } public static void waitForUpToDateJavaModel(long timeout, IProject project, IProgressMonitor monitor) { Job job = getNotificationJob(); if (job == null) { return; } monitor.subTask("Taking in account the resource changes of the previous builds" + project != null ? project.getName() : ""); long timeLimit = System.currentTimeMillis() + timeout; while (job.getState() != Job.NONE) { boolean stopWaiting = false; if (job.isBlocking()) { stopWaiting = true; } try { Thread.sleep(1000); } catch (InterruptedException e) { stopWaiting = true; } if (System.currentTimeMillis() > timeLimit) { stopWaiting = true; } if (stopWaiting) { break; } } } @Override protected IProject[] build(final int kind, @SuppressWarnings("rawtypes") Map args, IProgressMonitor mon) throws CoreException { final IProject project = getProject(); modelJ2C().ceylonModel().addProject(project.getProject()); final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); final IJavaProject javaProject = JavaCore.create(project); final ProgressMonitor$impl<IProgressMonitor>.Progress ceylonMonitor = utilJ2C().wrapProgressMonitor(new ProgressMonitorWrapper(mon) { @Override public boolean isCanceled() { return super.isCanceled() || PlatformUI.getWorkbench().isClosing(); } }).Progress$new$(1000, ceylon.language.String.instance("Ceylon build of project " + project.getName())); try { try { buildHook.startBuild(kind, args, project, getBuildConfig(), getContext(), ceylonMonitor.newChild(10).getWrapped()); } catch (CoreException e) { if (e.getStatus().getSeverity() == IStatus.CANCEL) { return project.getReferencedProjects(); } } try { IMarker[] buildMarkers = project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, true, DEPTH_ZERO); for (IMarker m: buildMarkers) { Object message = m.getAttribute(IMarker.MESSAGE); Object sourceId = m.getAttribute(IMarker.SOURCE_ID); if (message!=null && message.toString().endsWith("'.exploded'")) { //ignore message from JDT about missing JDTClasses dir m.delete(); } if (sourceId!=null && sourceId.equals(PLUGIN_ID)) { // Delete markers added by this builder since they will be added again just after. m.delete(); } else if (message!=null && message.toString().contains("is missing required Java project:")) { return project.getReferencedProjects(); } } List<IClasspathContainer> cpContainers = getCeylonClasspathContainers(javaProject); if (! preBuildChecks(project, javaProject, cpContainers)) { return project.getReferencedProjects(); } List<PhasedUnit> builtPhasedUnits = Collections.emptyList(); final BooleanHolder mustDoFullBuild = new BooleanHolder(); final BooleanHolder mustResolveClasspathContainer = new BooleanHolder(); final IResourceDelta currentDelta = getDelta(project); List<IResourceDelta> projectDeltas = new ArrayList<IResourceDelta>(); projectDeltas.add(currentDelta); for (IProject requiredProject : project.getReferencedProjects()) { projectDeltas.add(getDelta(requiredProject)); } boolean somethingToDo = chooseBuildTypeFromDeltas(kind, ceylonProject, projectDeltas, mustDoFullBuild, mustResolveClasspathContainer); if (!somethingToDo && (args==null || !args.containsKey(BUILDER_ID + ".reentrant"))) { return project.getReferencedProjects(); } if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } if (mustResolveClasspathContainer.value) { if (cpContainers != null) { buildHook.resolvingClasspathContainer(cpContainers); for (IClasspathContainer container: cpContainers) { if (container instanceof CeylonProjectModulesContainer) { CeylonProjectModulesContainer applicationModulesContainer = (CeylonProjectModulesContainer) container; boolean changed = applicationModulesContainer.resolveClasspath(ceylonMonitor.newChild(150), true); if(changed) { buildHook.setAndRefreshClasspathContainer(); JavaCore.setClasspathContainer(applicationModulesContainer.getPath(), new IJavaProject[]{javaProject}, new IClasspathContainer[]{null} , ceylonMonitor.newChild(25).getWrapped()); applicationModulesContainer.refreshClasspathContainer(ceylonMonitor.newChild(25)); } } } } } ceylonMonitor.updateRemainingWork(800); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } final TypeChecker typeChecker; Collection<IFile> filesForBinaryGeneration = Collections.emptyList(); if (mustDoFullBuild.value) { ceylonMonitor.changeTaskName("Full Ceylon build of project " + project.getName()); buildHook.doFullBuild(); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } cleanupModules(ceylonMonitor.getWrapped(), project); ceylonMonitor.worked(1); cleanupJdtClasses(ceylonMonitor.getWrapped(), project); ceylonMonitor.worked(1); ceylonMonitor.subTask("Clearing existing markers of project " + project.getName()); clearProjectMarkers(project, true, false); ceylonMonitor.worked(1); clearMarkersOn(project, true); ceylonMonitor.worked(1); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } //if (! getModelState(project).equals(ModelState.Parsed)) { if (!mustResolveClasspathContainer.value) { ceylonMonitor.subTask("Parsing source of project " + project.getName()); //if we already resolved the classpath, the //model has already been freshly-parsed buildHook.parseCeylonModel(); ceylonProject.parseCeylonModel( ceylonMonitor.newChild(96)); } typeChecker = ceylonProject.getTypechecker(); ceylonMonitor.updateRemainingWork(700); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } ceylonMonitor.subTask("Typechecking all source files of project " + project.getName()); builtPhasedUnits = fullTypeCheck(ceylonProject, typeChecker, ceylonMonitor.newChild(300)); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } filesForBinaryGeneration = list(IFile.class,ceylonProject.getProjectNativeFiles()); } else { buildHook.doIncrementalBuild(); typeChecker = ceylonProject.getTypechecker(); PhasedUnits phasedUnits = typeChecker.getPhasedUnits(); List<IFile> filesToRemove = new ArrayList<IFile>(); Set<IFile> changedFiles = new HashSet<IFile>(); ceylonMonitor.getWrapped().setTaskName("Incremental Ceylon build of project " + project.getName()); ceylonMonitor.subTask("Scanning deltas of project " + project.getName()); scanChanges(ceylonProject, currentDelta, projectDeltas, filesToRemove, changedFiles); ceylonMonitor.worked(9); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } if (!isModelTypeChecked(project)) { buildHook.fullTypeCheckDuringIncrementalBuild(); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } ceylonMonitor.subTask("Clearing existing markers of project (except backend errors)" + project.getName()); clearProjectMarkers(project, true, false); clearMarkersOn(project, false); ceylonMonitor.worked(1); ceylonMonitor.subTask("Initial typechecking all source files of project " + project.getName()); builtPhasedUnits = fullTypeCheck(ceylonProject, typeChecker, ceylonMonitor.newChild(170)); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } ceylonMonitor.subTask("Collecting dependencies of project " + project.getName()); // getConsoleStream().println(timedMessage("Collecting dependencies")); collectDependencies(project, typeChecker, builtPhasedUnits); ceylonMonitor.worked(17); ceylonMonitor.subTask("Collecting problems for project " + project.getName()); addProblemAndTaskMarkers(builtPhasedUnits, project); ceylonMonitor.worked(3); } ceylonMonitor.updateRemainingWork(600); ceylonMonitor.subTask("Scanning dependencies of deltas of project " + project.getName()); final Set<IFile> filesToCompile = new HashSet<>(); final Set<IFile> filesToTypecheck = new HashSet<>(); calculateDependencies(ceylonProject, currentDelta, changedFiles, typeChecker, phasedUnits, filesToTypecheck, filesToCompile, ceylonMonitor); ceylonMonitor.worked(10); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } ceylonMonitor.subTask("Cleaning files and markers for project " + project.getName()); cleanRemovedFilesFromCeylonModel(filesToRemove, phasedUnits, ceylonProject); ceylonMonitor.worked(3); cleanRemovedFilesFromOutputs(filesToRemove, ceylonProject); ceylonMonitor.worked(4); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } buildHook.incrementalBuildSources(changedFiles, filesToRemove, filesToTypecheck); clearProjectMarkers(project, true, false); ceylonMonitor.worked(1); clearMarkersOn(filesToTypecheck, true); ceylonMonitor.worked(1); clearMarkersOn(filesToCompile, true, true); ceylonMonitor.worked(1); ceylonMonitor.subTask("Compiling " + filesToTypecheck.size() + " source files in project " + project.getName()); builtPhasedUnits = doWithCeylonModelCaching(new Callable<List<PhasedUnit>>() { @Override public List<PhasedUnit> call() throws Exception { return incrementalBuild(ceylonProject, filesToTypecheck, ceylonMonitor.newChild(190)); } }); if (builtPhasedUnits.isEmpty() && filesToTypecheck.isEmpty() && filesToCompile.isEmpty()) { return project.getReferencedProjects(); } if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } buildHook.incrementalBuildResult(builtPhasedUnits); filesForBinaryGeneration = filesToCompile; } clearProjectMarkers(project, false, true); ceylonMonitor.updateRemainingWork(400); ceylonMonitor.subTask("Collecting problems for project " + project.getName()); addProblemAndTaskMarkers(builtPhasedUnits, project); ceylonMonitor.worked(3); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } ceylonMonitor.subTask("Collecting dependencies of project " + project.getName()); // getConsoleStream().println(timedMessage("Collecting dependencies")); collectDependencies(project, typeChecker, builtPhasedUnits); ceylonMonitor.worked(17); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } buildHook.beforeGeneratingBinaries(); ceylonMonitor.subTask("Generating binaries for project " + project.getName()); final Collection<IFile> filesToProcess = filesForBinaryGeneration; final Collection<PhasedUnit> unitsTypecheckedIncrementally = mustDoFullBuild.value ? Collections.<PhasedUnit>emptyList() : builtPhasedUnits; cleanChangedFilesFromExplodedDirectory(filesToProcess, ceylonProject); doWithCeylonModelCaching(new Callable<Boolean>() { @Override public Boolean call() throws CoreException { return generateBinaries(javaProject, unitsTypecheckedIncrementally, filesToProcess, typeChecker, ceylonMonitor.newChild(370)); } }); buildHook.afterGeneratingBinaries(); if (ceylonMonitor.isCancelled()) { throw new OperationCanceledException(); } if (isExplodeModulesEnabled(project)) { ceylonMonitor.subTask("Rebuilding using exploded modules directory of " + project.getName()); sheduleIncrementalRebuild(args, project, ceylonMonitor.newChild(10)); } return project.getReferencedProjects(); } catch(OperationCanceledException e) { e.printStackTrace(); throw e; } finally { buildHook.endBuild(); } } finally { ceylonMonitor.destroy(null); } } /* * Checks for global build error and add the relevant markers. * * Returns true if the build can continue, and false if the build should stop. * */ private boolean preBuildChecks(final IProject project, final IJavaProject javaProject, List<IClasspathContainer> cpContainers) throws CoreException, JavaModelException { CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(javaProject.getProject()); boolean languageModuleContainerFound = false; boolean applicationModulesContainerFound = false; boolean buildCanContinue = true; for (IClasspathContainer container : cpContainers) { if (container instanceof CeylonLanguageModuleContainer) { languageModuleContainerFound = true; } if (container instanceof CeylonProjectModulesContainer) { applicationModulesContainerFound = true; } } if (! languageModuleContainerFound) { //if the ClassPathContainer is missing, add an error IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "The Ceylon classpath container for the language module is not set on the project " + " (try running Enable Ceylon Builder on the project)"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); buildCanContinue = false; } if (! applicationModulesContainerFound) { //if the ClassPathContainer is missing, add an error IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "The Ceylon classpath container for application modules is not set on the project " + " (try running Enable Ceylon Builder on the project)"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); buildCanContinue = false; } /* Begin issue #471 */ ICommand[] builders = project.getDescription().getBuildSpec(); int javaOrder=0, ceylonOrder = 0; for (int n=0; n<builders.length; n++) { if (builders[n].getBuilderName().equals(JavaCore.BUILDER_ID)) { javaOrder = n; } else if (builders[n].getBuilderName().equals(CeylonBuilder.BUILDER_ID)) { ceylonOrder = n; } } if (ceylonOrder < javaOrder) { //if the build order is not correct, add an error and return IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "The Ceylon Builder should run after the Java Builder. Change the order of builders in the project properties"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, "Project " + project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); buildCanContinue = false; } /* End issue #471 */ boolean sourceDirectoryInProjectFolder = false; boolean outputDirectoryInProjectFolder = javaProject.getOutputLocation().equals(javaProject.getPath()); for (IPackageFragmentRoot root : javaProject.getAllPackageFragmentRoots()) { if (root.getRawClasspathEntry().getEntryKind() == IClasspathEntry.CPE_SOURCE && root.getResource().getLocation().equals(project.getLocation())) { sourceDirectoryInProjectFolder = true; break; } } if (sourceDirectoryInProjectFolder || outputDirectoryInProjectFolder) { if (sourceDirectoryInProjectFolder) { IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "One source directory is the root folder of the project, which is not supported for Ceylon projects." + " Change it in the project properties"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, "Project " + project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); } if (outputDirectoryInProjectFolder) { IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "The project Java class directory is the root folder of the project, which is not supported for Ceylon projects." + " Change it in the project properties"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, "Project " + project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); } buildCanContinue = false; } IPath modulesOutputFolderPath = getCeylonModulesOutputFolder(project).getRawLocation(); IPath jdtOutputFolderPath = javaProject.getOutputLocation(); IFolder jdtOutputFolder = project.getWorkspace().getRoot().getFolder(jdtOutputFolderPath); if (jdtOutputFolder.exists()) { jdtOutputFolderPath = jdtOutputFolder.getRawLocation(); } if (modulesOutputFolderPath.isPrefixOf(jdtOutputFolderPath) || jdtOutputFolderPath.isPrefixOf(modulesOutputFolderPath)) { //if the build order is not correct, add an error and return IMarker marker = project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER); marker.setAttribute(IMarker.MESSAGE, "The Ceylon modules output directory and Java class directory shoudln't collide." + " Change one of them in the project properties"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, "Project " + project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); buildCanContinue = false; } if (! ceylonProject.getSynchronizedWithConfiguration()) { //if the build order is not correct, add an error and return IMarker marker = project.createMarker(CEYLON_CONFIG_NOT_IN_SYNC_MARKER); marker.setAttribute(IMarker.MESSAGE, "The Ceylon Build Paths are not in sync with those in the ceylon configuration file (" + "./ceylon/config)\n" + "Either modify this file or change the build paths accordingly in the project properties"); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.LOCATION, project.getName()); marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID); buildCanContinue = false; } if (project.findMarkers(CHARSET_PROBLEM_MARKER_ID, false, IResource.DEPTH_ZERO).length > 0) { buildCanContinue = false; } return buildCanContinue; } private void sheduleIncrementalRebuild(@SuppressWarnings("rawtypes") Map args, final IProject project, ProgressMonitorChild<IProgressMonitor> monitor) { try { getCeylonClassesOutputFolder(project).refreshLocal(DEPTH_INFINITE, monitor.getWrapped()); } catch (CoreException e) { e.printStackTrace(); }//monitor); if (args==null || !args.containsKey(BUILDER_ID + ".reentrant")) { buildHook.scheduleReentrantBuild(); final CeylonBuildHook currentBuildHook = buildHook; Job job = new Job("Rebuild with Ceylon classes") { @Override protected IStatus run(IProgressMonitor monitor) { try { //we have already done a build of both the Java and Ceylon classes //so now go back and try to build the both the Java and Ceylon //classes again, using the classes we previously generated - this //is to allow references from Java to Ceylon project.build(INCREMENTAL_BUILD, JavaCore.BUILDER_ID, null, monitor); Map<String,String> map = new HashMap<String,String>(); map.put(BUILDER_ID + ".reentrant", "true"); project.build(INCREMENTAL_BUILD, BUILDER_ID, map, monitor); currentBuildHook.afterReentrantBuild(); } catch (CoreException e) { e.printStackTrace(); } return Status.OK_STATUS; } }; job.setRule(project.getWorkspace().getRoot()); job.schedule(); } } private void collectDependencies(IProject project, TypeChecker typeChecker, List<PhasedUnit> builtPhasedUnits) throws CoreException { for (PhasedUnit pu : builtPhasedUnits) { new UnitDependencyVisitor(pu).visit(pu.getCompilationUnit()); } } private void cleanRemovedFilesFromCeylonModel(Collection<IFile> filesToRemove, PhasedUnits phasedUnits, CeylonProject<IProject, IResource, IFolder, IFile> project) { for (IFile fileToRemove: filesToRemove) { if(isCeylon(fileToRemove)) { // Remove the ceylon phasedUnit (which will also remove the unit from the package) PhasedUnit phasedUnitToDelete = phasedUnits.getPhasedUnit(vfsJ2C().createVirtualResource(fileToRemove, project.getIdeArtifact())); if (phasedUnitToDelete != null) { assert(phasedUnitToDelete instanceof ProjectPhasedUnit); ((ProjectPhasedUnit) phasedUnitToDelete).remove(); } } else if (isJava(fileToRemove)) { // Remove the external unit from the package Package pkg = getPackage(fileToRemove); if (pkg != null) { for (Unit unitToTest: pkg.getUnits()) { if (unitToTest.getFilename().equals(fileToRemove.getName())) { assert(unitToTest instanceof JavaUnit); JavaUnit javaUnit = (JavaUnit) unitToTest; javaUnit.remove(); break; } } } } } } private void calculateDependencies(CeylonProject<IProject, IResource,IFolder, IFile> ceylonProject, IResourceDelta currentDelta, Collection<IFile> changedFiles, TypeChecker typeChecker, PhasedUnits phasedUnits, Set<IFile> filesToTypeCheck, Set<IFile> filesToCompile, Cancellable cancellable) { IProject project = ceylonProject.getIdeArtifact(); Set<IFile> filesToAddInTypecheck = new HashSet<IFile>(); Set<IFile> filesToAddInCompile = new HashSet<IFile>(); if (!changedFiles.isEmpty()) { Set<IFile> allTransitivelyDependingFiles = searchForDependantFiles( project, changedFiles, typeChecker, cancellable, false); Set<IFile> dependingFilesAccordingToStructureDelta; boolean astAwareIncrementalBuild = areAstAwareIncrementalBuildsEnabled(project); if (astAwareIncrementalBuild) { dependingFilesAccordingToStructureDelta = searchForDependantFiles( project, changedFiles, typeChecker, cancellable, true); } else { dependingFilesAccordingToStructureDelta = allTransitivelyDependingFiles; } if (cancellable.isCancelled()) { throw new OperationCanceledException(); } for (PhasedUnit phasedUnit : phasedUnits.getPhasedUnits()) { Unit unit = phasedUnit.getUnit(); if (unit.getUnresolvedReferences()) { IFile fileToAdd = vfsJ2C().getIFileVirtualFile(phasedUnit.getUnitFile()).getNativeResource(); if (fileToAdd.exists()) { filesToAddInTypecheck.add(fileToAdd); filesToAddInCompile.add(fileToAdd); } } Set<Declaration> duplicateDeclarations = unit.getDuplicateDeclarations(); if (!duplicateDeclarations.isEmpty()) { IFile fileToAdd = vfsJ2C().getIFileVirtualFile(phasedUnit.getUnitFile()).getNativeResource(); if (fileToAdd.exists()) { filesToAddInTypecheck.add(fileToAdd); filesToAddInCompile.add(fileToAdd); } for (Declaration duplicateDeclaration : duplicateDeclarations) { Unit duplicateUnit = duplicateDeclaration.getUnit(); if ((duplicateUnit instanceof SourceFile) && (duplicateUnit instanceof IResourceAware)) { IResourceAware<IProject,IFolder,IFile> ra = (IResourceAware<IProject,IFolder,IFile>) duplicateUnit; IFile duplicateDeclFile = ra.getResourceFile(); if (duplicateDeclFile != null && duplicateDeclFile.exists()) { filesToAddInTypecheck.add(duplicateDeclFile); filesToAddInCompile.add(duplicateDeclFile); } } } } } if (cancellable.isCancelled()) { throw new OperationCanceledException(); } for (IFile f: allTransitivelyDependingFiles) { if (f.getProject() == project) { if (isSourceFile(f) || isResourceFile(f)) { if (f.exists()) { filesToAddInTypecheck.add(f); if (!astAwareIncrementalBuild || dependingFilesAccordingToStructureDelta.contains(f) || isJava(f)) { filesToAddInCompile.add(f); } } else { // If the file is moved : add a dependency on the new file if (currentDelta != null) { IResourceDelta removedFile = currentDelta.findMember(f.getProjectRelativePath()); if (removedFile != null && (removedFile.getFlags() & IResourceDelta.MOVED_TO) != 0 && removedFile.getMovedToPath() != null) { IFile movedFile = project.getFile(removedFile.getMovedToPath().removeFirstSegments(1)); if (isSourceFile(movedFile) || isResourceFile(movedFile)) { filesToAddInTypecheck.add(movedFile); if (!astAwareIncrementalBuild || dependingFilesAccordingToStructureDelta.contains(movedFile)) { filesToAddInCompile.add(movedFile); } } } } } } } } } for (IFile file : list(IFile.class, ceylonProject.getProjectNativeFiles())) { try { boolean backendErrorOrMissingClassFile = false; for (IMarker marker : file.findMarkers(PROBLEM_MARKER_ID + ".backend", false, IResource.DEPTH_ZERO)) { Object attribute = marker.getAttribute(IMarker.SEVERITY); if (attribute instanceof Integer) { switch (((Integer)attribute).intValue()) { case IMarker.SEVERITY_ERROR: // For backend errors case IMarker.SEVERITY_INFO: // For missing class files backendErrorOrMissingClassFile = true; } if (backendErrorOrMissingClassFile) { filesToAddInTypecheck.add(file); filesToAddInCompile.add(file); break; } } } } catch (CoreException e) { e.printStackTrace(); filesToAddInTypecheck.add(file); filesToAddInCompile.add(file); } } filesToTypeCheck.addAll(filesToAddInTypecheck); filesToCompile.addAll(filesToAddInCompile); } private Set<IFile> searchForDependantFiles(IProject project, Collection<IFile> changedFiles, TypeChecker typeChecker, Cancellable cancellable, boolean filterAccordingToStructureDelta) { Set<IFile> changeDependents= new HashSet<IFile>(); Set<IFile> analyzedFiles= new HashSet<IFile>(); changeDependents.addAll(changedFiles); boolean changed = false; do { Collection<IFile> additions= new HashSet<IFile>(); for (Iterator<IFile> iter=changeDependents.iterator(); iter.hasNext();) { final IFile srcFile= iter.next(); if (analyzedFiles.contains(srcFile)) { continue; } analyzedFiles.add(srcFile); IProject currentFileProject = srcFile.getProject(); CeylonProject<IProject, IResource, IFolder, IFile> currentFileCeylonProject = modelJ2C().ceylonModel().getProject(currentFileProject); TypeChecker currentFileTypeChecker = null; if (currentFileProject == project) { currentFileTypeChecker = typeChecker; } else { currentFileTypeChecker = getProjectTypeChecker(currentFileProject); } if (! CeylonBuilder.isInSourceFolder(srcFile)) { // Don't search dependencies inside resource folders. continue; } if (filterAccordingToStructureDelta) { IResourceAware unit = getUnit(srcFile); if (unit instanceof ProjectSourceFile) { ProjectSourceFile projectSourceFile = (ProjectSourceFile) unit; if (projectSourceFile.getDependentsOf().size() > 0) { CompilationUnitDelta delta = projectSourceFile.buildDeltaAgainstModel(); if (delta != null && delta.getChanges().getSize() == 0 && delta.getChildrenDeltas().getSize() == 0) { continue; } } } } Set<String> filesDependingOn = getDependentsOf(srcFile, currentFileTypeChecker, currentFileCeylonProject); for (String dependingFile: filesDependingOn) { if (cancellable.isCancelled()) { throw new OperationCanceledException(); } //TODO: note that the following is slightly // fragile - it depends on the format // of the path that we use to track // dependents! IPath pathRelativeToProject = new Path(dependingFile); //.makeRelativeTo(project.getLocation()); IFile depFile= (IFile) project.findMember(pathRelativeToProject); if (depFile == null) { depFile= (IFile) currentFileProject.findMember(dependingFile); } if (depFile != null) { additions.add(depFile); } else { System.err.println("could not resolve dependent unit: " + dependingFile); } } } changed = changeDependents.addAll(additions); } while (changed && !filterAccordingToStructureDelta); return changeDependents; } private void scanChanges(final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, final IResourceDelta currentDelta, List<IResourceDelta> projectDeltas, final List<IFile> filesToRemove, final Set<IFile> changedSources) throws CoreException { for (final IResourceDelta projectDelta: projectDeltas) { if (projectDelta != null) { final IProject project = (IProject) projectDelta.getResource(); List<IFolder> allRootFolders = new ArrayList<>(); allRootFolders.addAll(getSourceFolders(project)); allRootFolders.addAll(getResourceFolders(project)); ceylonProject.getRootFolders(); final Iterable<FolderVirtualFile<IProject, IResource, IFolder, IFile>> iter = (Iterable<FolderVirtualFile<IProject, IResource, IFolder, IFile>>) new JavaIterable<FolderVirtualFile<IProject, IResource, IFolder, IFile>>( td(FolderVirtualFile.class), (ceylon.language.Iterable<? extends FolderVirtualFile<IProject, IResource, IFolder, IFile>, ? extends Object>) ceylonProject.getRootFolders()); for (final FolderVirtualFile<IProject, IResource, IFolder, IFile> rootVirtualFolder : iter) { IFolder rootFolder = rootVirtualFolder.getNativeResource(); IResourceDelta affectedRoot = projectDelta.findMember(rootFolder.getProjectRelativePath()); if (affectedRoot != null) { RootFolderType rootFolderType = getRootFolderType(rootFolder); final boolean inSourceDirectory = rootFolderType == RootFolderType.SOURCE; final boolean inResourceDirectory = rootFolderType == RootFolderType.RESOURCE; if (inResourceDirectory || inSourceDirectory) { // a real Ceylon source or resource folder so scan for changes affectedRoot.accept(new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if (resource instanceof IFile) { IFile file= (IFile) resource; if (inResourceDirectory || (isCompilable(file) && inSourceDirectory) ) { changedSources.add(file); if (projectDelta == currentDelta) { if (delta.getKind() == IResourceDelta.REMOVED) { filesToRemove.add(file); ceylonProject.removeFileFromModel(file); } if (delta.getKind() == IResourceDelta.ADDED) { IFile addedFile = (IFile) resource; ceylonProject.addFileToModel(addedFile); } } } return false; } if (resource instanceof IFolder) { IFolder folder= (IFolder) resource; if (projectDelta == currentDelta) { if (folder.exists() && delta.getKind() != IResourceDelta.REMOVED) { if (getPackage(folder) == null || getRootFolder(folder) == null) { ceylonProject.addFolderToModel(folder); } } } } return true; } }); } } } } } } public boolean chooseBuildTypeFromDeltas(final int kind, final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, final List<IResourceDelta> currentDeltas, final BooleanHolder mustDoFullBuild, final BooleanHolder mustResolveClasspathContainer) { mustDoFullBuild.value = kind == FULL_BUILD || kind == CLEAN_BUILD || !ceylonProject.getParsed(); mustResolveClasspathContainer.value = kind==FULL_BUILD; //false; final BooleanHolder somethingToBuild = new BooleanHolder(); if (JavaProjectStateMirror.hasClasspathChanged(ceylonProject.getIdeArtifact())) { mustDoFullBuild.value = true; } if (!mustDoFullBuild.value || !mustResolveClasspathContainer.value) { for (IResourceDelta currentDelta: currentDeltas) { if (currentDelta != null) { try { currentDelta.accept(new DeltaScanner(mustDoFullBuild, ceylonProject, somethingToBuild, mustResolveClasspathContainer)); } catch (CoreException e) { e.printStackTrace(); mustDoFullBuild.value = true; mustResolveClasspathContainer.value = true; } } else { mustDoFullBuild.value = true; mustResolveClasspathContainer.value = true; } } } class DecisionMaker { public boolean mustContinueBuild() { return mustDoFullBuild.value || mustResolveClasspathContainer.value || somethingToBuild.value || ! ceylonProject.getTypechecked(); } }; DecisionMaker decisionMaker = new DecisionMaker(); buildHook.deltasAnalyzed(currentDeltas, somethingToBuild, mustDoFullBuild, mustResolveClasspathContainer, decisionMaker.mustContinueBuild()); return decisionMaker.mustContinueBuild(); } // private static String successMessage(boolean binariesGenerationOK) { // return " " + (binariesGenerationOK ? // "...binary generation succeeded" : "...binary generation FAILED"); // } private Set<String> getDependentsOf(IFile srcFile, TypeChecker currentFileTypeChecker, CeylonProject<IProject, IResource, IFolder, IFile> currentFileCeylonProject) { if (isCeylon(srcFile)) { PhasedUnit phasedUnit = currentFileTypeChecker.getPhasedUnits() .getPhasedUnit(vfsJ2C().createVirtualResource(srcFile, currentFileCeylonProject.getIdeArtifact())); if (phasedUnit != null && phasedUnit.getUnit() != null) { return phasedUnit.getUnit().getDependentsOf(); } } else { Unit unit = getJavaUnit(currentFileCeylonProject.getIdeArtifact(), srcFile); if (unit instanceof JavaCompilationUnit) { return unit.getDependentsOf(); } } return Collections.emptySet(); } @SuppressWarnings("unchecked") static ProjectPhasedUnit<IProject, IResource, IFolder, IFile> parseFileToPhasedUnit(final ModuleManager moduleManager, final ModuleSourceMapper moduleSourceMapper, final TypeChecker typeChecker, final FileVirtualFile<IProject, IResource, IFolder, IFile> file, final FolderVirtualFile<IProject, IResource, IFolder, IFile> srcDir, final Package pkg) { return (ProjectPhasedUnit<IProject, IResource, IFolder, IFile>) new ProjectSourceParser<IProject, IResource, IFolder, IFile>( td(IProject.class), td(IResource.class), td(IFolder.class), td(IFile.class), file.getCeylonProject(), file, srcDir).parseFileToPhasedUnit(moduleManager, typeChecker, file, srcDir, pkg); } private List<PhasedUnit> incrementalBuild(final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, final Collection<IFile> sourceToCompile, final ProgressMonitor<IProgressMonitor> mon) { final IProject project = ceylonProject.getIdeArtifact(); final TypeChecker typeChecker = ceylonProject.getTypechecker(); final PhasedUnits pus = typeChecker.getPhasedUnits(); final BaseIdeModuleManager moduleManager = (BaseIdeModuleManager) pus.getModuleManager(); final BaseIdeModuleSourceMapper moduleSourceMapper = (BaseIdeModuleSourceMapper) pus.getModuleSourceMapper(); final BaseIdeModelLoader modelLoader = getModelLoader(typeChecker); return doWithSourceModel(project, false, 20, new Callable<List<PhasedUnit>>() { @Override public List<PhasedUnit> call() { List<BaseIdeModule> modulesToRefresh = new ArrayList<>(); for (Module m : typeChecker.getContext().getModules().getListOfModules()) { if (m instanceof BaseIdeModule) { BaseIdeModule module = (BaseIdeModule) m; if (module.getIsCeylonArchive()) { modulesToRefresh.add(module); } } } long ceylonArchivesRefreshingTicks = modulesToRefresh.size()*10; long sourceTypecheckingTicks = sourceToCompile.size()*10; long dependenciesTypecheckingTicks = typeChecker.getPhasedUnitsOfDependencies().size()*6; long sourceUpdatingTicks = sourceToCompile.size()*4; final ProgressMonitor$impl<IProgressMonitor>.Progress progress = mon.Progress$new$( ceylonArchivesRefreshingTicks + dependenciesTypecheckingTicks + sourceUpdatingTicks + sourceTypecheckingTicks , null); try { // First refresh the modules that are cross-project references to sources modules // in referenced projects. This will : // - clean the binary declarations and reload the class-to-source mapping file for binary-based modules, // - remove old PhasedUnits and parse new or updated PhasedUnits from the source archive for source-based modules progress.changeTaskName("RefreshingCeylonArchives"); for (BaseIdeModule moduleToRefresh : modulesToRefresh) { moduleToRefresh.refresh(); progress.worked(10); } // Secondly typecheck again the changed PhasedUnits in changed external source modules // (those which come from referenced projects) List<PhasedUnits> phasedUnitsOfDependencies = typeChecker.getPhasedUnitsOfDependencies(); List<PhasedUnit> dependencies = new ArrayList<PhasedUnit>(); for (PhasedUnits phasedUnits: phasedUnitsOfDependencies) { for (PhasedUnit phasedUnit: phasedUnits.getPhasedUnits()) { dependencies.add(phasedUnit); } } for (PhasedUnit pu: dependencies) { progress.subTask("scanning declarations in " + pu.getUnit().getFilename()); pu.scanDeclarations(); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu: dependencies) { progress.subTask("scanning types in " + pu.getUnit().getFilename()); pu.scanTypeDeclarations(Cancellable.ALWAYS_CANCELLED); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } for (PhasedUnit pu: dependencies) { progress.subTask("validating refinement in " + pu.getUnit().getFilename()); pu.validateRefinement(); //TODO: only needed for type hierarchy view in IDE! if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu: dependencies) { progress.subTask("analysing types in " + pu.getUnit().getFilename()); pu.analyseTypes(Cancellable.ALWAYS_CANCELLED); // Needed to have the right values in the Value.trans field (set in Expression visitor) // which in turn is important for debugging ! if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } // Then typecheck the changed source of this project Set<String> cleanedPackages = new HashSet<String>(); List<PhasedUnit> phasedUnitsToUpdate = new ArrayList<PhasedUnit>(); progress.changeTaskName( "Updating " + sourceToCompile.size() + " source files of project " + project.getName()); for (IFile fileToUpdate : sourceToCompile) { if (progress.isCancelled()) { throw new OperationCanceledException(); } // skip non-ceylon files if(!isCeylon(fileToUpdate)) { if (isJava(fileToUpdate)) { Unit toRemove = getJavaUnit(project, fileToUpdate); if(toRemove instanceof JavaUnit) { ((JavaUnit) toRemove).update(); } else { String packageName = getPackageName(fileToUpdate); if (! cleanedPackages.contains(packageName)) { modelLoader.clearCachesOnPackage(packageName); cleanedPackages.add(packageName); } } } progress.worked(4); continue; } FileVirtualFile<IProject, IResource, IFolder, IFile> file = vfsJ2C().createVirtualFile(fileToUpdate, ceylonProject.getIdeArtifact()); IFolder srcFolder = getRootFolder(fileToUpdate); ProjectPhasedUnit alreadyBuiltPhasedUnit = (ProjectPhasedUnit) pus.getPhasedUnit(file); Package pkg = null; if (alreadyBuiltPhasedUnit!=null) { // Editing an already built file pkg = alreadyBuiltPhasedUnit.getPackage(); } else { IFolder packageFolder = (IFolder) file.getNativeResource().getParent(); pkg = getPackage(packageFolder); } if (srcFolder == null || pkg == null) { progress.worked(4); continue; } FolderVirtualFile<IProject, IResource, IFolder, IFile> srcDir = vfsJ2C().createVirtualFolder(project, srcFolder.getProjectRelativePath()); PhasedUnit newPhasedUnit = parseFileToPhasedUnit(moduleManager, moduleSourceMapper, typeChecker, file, srcDir, pkg); phasedUnitsToUpdate.add(newPhasedUnit); progress.worked(4); } if (progress.isCancelled()) { throw new OperationCanceledException(); } if (phasedUnitsToUpdate.size() == 0) { return phasedUnitsToUpdate; } progress.changeTaskName( "Typechecking " + sourceToCompile.size() + " source files of project " + project.getName()); for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { assert(phasedUnit instanceof ProjectPhasedUnit); ((ProjectPhasedUnit)phasedUnit).install(); progress.worked(1); } modelLoader.setupSourceFileObjects(phasedUnitsToUpdate); if (progress.isCancelled()) { throw new OperationCanceledException(); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { if (! phasedUnit.isDeclarationsScanned()) { phasedUnit.validateTree(); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { phasedUnit.visitSrcModulePhase(); if (progress.isCancelled()) { throw new OperationCanceledException(); } } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { phasedUnit.visitRemainingModulePhase(); if (progress.isCancelled()) { throw new OperationCanceledException(); } } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { if (! phasedUnit.isDeclarationsScanned()) { progress.subTask("scanning declarations " + phasedUnit.getUnit().getFilename()); phasedUnit.scanDeclarations(); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { if (! phasedUnit.isTypeDeclarationsScanned()) { progress.subTask("scanning types " + phasedUnit.getUnit().getFilename()); phasedUnit.scanTypeDeclarations(Cancellable.ALWAYS_CANCELLED); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { progress.subTask("validating refinement in " + phasedUnit.getUnit().getFilename()); if (! phasedUnit.isRefinementValidated()) { phasedUnit.validateRefinement(); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { if (! phasedUnit.isFullyTyped()) { progress.subTask("typechecking " + phasedUnit.getUnit().getFilename()); phasedUnit.analyseTypes(Cancellable.ALWAYS_CANCELLED); if (showWarnings(project)) { phasedUnit.analyseUsage(); } } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(3); } for (PhasedUnit phasedUnit : phasedUnitsToUpdate) { phasedUnit.analyseFlow(); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } UnknownTypeCollector utc = new UnknownTypeCollector(); for (PhasedUnit pu : phasedUnitsToUpdate) { pu.getCompilationUnit().visit(utc); } if (progress.isCancelled()) { throw new OperationCanceledException(); } return phasedUnitsToUpdate; } finally { progress.destroy(null); } } }); } private Unit getJavaUnit(IProject project, IFile fileToUpdate) { IJavaElement javaElement = (IJavaElement) fileToUpdate.getAdapter(IJavaElement.class); if (javaElement instanceof ICompilationUnit) { ICompilationUnit compilationUnit = (ICompilationUnit) javaElement; IJavaElement packageFragment = compilationUnit.getParent(); BaseIdeModelLoader projectModelLoader = getProjectModelLoader(project); // TODO : Why not use the Model Loader cache to get the declaration // instead of iterating through all the packages ? if (projectModelLoader != null) { Package pkg = projectModelLoader.findPackage(packageFragment.getElementName()); if (pkg != null) { for (Declaration decl : pkg.getMembers()) { Unit unit = decl.getUnit(); if (unit.getFilename().equals(fileToUpdate.getName())) { return unit; } } } } } return null; } private List<PhasedUnit> fullTypeCheck( final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, final TypeChecker typeChecker, final ProgressMonitor<IProgressMonitor> mon) throws CoreException { ceylonProject.setState(ProjectState.getProjectState$typechecking()); final IProject project = ceylonProject.getIdeArtifact(); List<PhasedUnit> builtPhasedUnits = doWithCeylonModelCaching(new Callable<List<PhasedUnit>>() { @Override public List<PhasedUnit> call() throws CoreException { final List<PhasedUnits> phasedUnitsOfDependencies = typeChecker.getPhasedUnitsOfDependencies(); final List<PhasedUnit> dependencies = new ArrayList<PhasedUnit>(); for (PhasedUnits phasedUnits: phasedUnitsOfDependencies) { for (PhasedUnit phasedUnit: phasedUnits.getPhasedUnits()) { dependencies.add(phasedUnit); } } final List<PhasedUnit> listOfUnits = typeChecker.getPhasedUnits().getPhasedUnits(); final ProgressMonitor$impl<IProgressMonitor>.Progress progress = mon.Progress$new$(dependencies.size()*6+listOfUnits.size()*8, ceylon.language.String.instance( "Typechecking " + listOfUnits.size() + " source files of project " + project.getName())); try { final BaseIdeModelLoader loader = getModelLoader(typeChecker); return doWithSourceModel(project, false, 20, new Callable<List<PhasedUnit>>() { @Override public List<PhasedUnit> call() { for (PhasedUnit pu: dependencies) { progress.subTask("scanning declarations in " + pu.getUnit().getFilename()); pu.scanDeclarations(); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu: dependencies) { progress.subTask("scanning types in " + pu.getUnit().getFilename()); pu.scanTypeDeclarations(Cancellable.ALWAYS_CANCELLED); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } for (PhasedUnit pu: dependencies) { progress.subTask("validating refinement in " + pu.getUnit().getFilename()); pu.validateRefinement(); //TODO: only needed for type hierarchy view in IDE! if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu: dependencies) { progress.subTask("analysing types in " + pu.getUnit().getFilename()); pu.analyseTypes(Cancellable.ALWAYS_CANCELLED); // Needed to have the right values in the Value.trans field (set in Expression visitor) // which in turn is important for debugging ! if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } progress.subTask("loading language module packages for project " + project.getName()); Module languageModule = loader.getLanguageModule(); loader.loadPackage(languageModule, "com.redhat.ceylon.compiler.java.metadata", true); loader.loadPackage(languageModule, LANGUAGE_MODULE_NAME, true); loader.loadPackage(languageModule, "ceylon.language.descriptor", true); loader.loadPackageDescriptors(); progress.subTask("typechecking source files for project " + project.getName()); for (PhasedUnit pu : listOfUnits) { if (! pu.isDeclarationsScanned()) { progress.subTask("scanning declarations in " + pu.getUnit().getFilename()); pu.validateTree(); pu.scanDeclarations(); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu : listOfUnits) { if (! pu.isTypeDeclarationsScanned()) { progress.subTask("scanning types in " + pu.getUnit().getFilename()); pu.scanTypeDeclarations(Cancellable.ALWAYS_CANCELLED); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(2); } for (PhasedUnit pu: listOfUnits) { if (! pu.isRefinementValidated()) { progress.subTask("validating refinement in " + pu.getUnit().getFilename()); pu.validateRefinement(); } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } for (PhasedUnit pu : listOfUnits) { if (! pu.isFullyTyped()) { progress.subTask("analysing types in " + pu.getUnit().getFilename()); pu.analyseTypes(Cancellable.ALWAYS_CANCELLED); if (showWarnings(project)) { pu.analyseUsage(); } } if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(3); } for (PhasedUnit pu: listOfUnits) { progress.subTask("analysing flow in " + pu.getUnit().getFilename()); pu.analyseFlow(); if (progress.isCancelled()) { throw new OperationCanceledException(); } progress.worked(1); } UnknownTypeCollector utc = new UnknownTypeCollector(); for (PhasedUnit pu : listOfUnits) { pu.getCompilationUnit().visit(utc); } progress.subTask(""); return typeChecker.getPhasedUnits().getPhasedUnits(); } }); } finally { progress.destroy(null); } } }); ceylonProject.setState(ProjectState.getProjectState$typechecked()); return builtPhasedUnits; } private static void addProblemAndTaskMarkers(final List<PhasedUnit> units, final IProject project) { for (PhasedUnit phasedUnit: units) { ProjectPhasedUnit<IProject,IResource,IFolder,IFile> projectPhasedUnit = (ProjectPhasedUnit<IProject,IResource,IFolder,IFile>)phasedUnit; IFile file = projectPhasedUnit.getResourceFile(); CompilationUnit compilationUnit = phasedUnit.getCompilationUnit(); compilationUnit.visit(new WarningSuppressionVisitor<Warning>(Warning.class, getSuppressedWarnings(project))); compilationUnit.visit(new MarkerCreator(file)); addTaskMarkers(file, phasedUnit.getTokens()); } } private boolean generateBinaries(IJavaProject javaProject, Collection<PhasedUnit> unitsTypecheckedIncrementally, Collection<IFile> filesToCompile, TypeChecker typeChecker, ProgressMonitor<IProgressMonitor> monitor) throws CoreException { List<String> options = new ArrayList<String>(); List<File> js_srcdir = new ArrayList<File>(); List<File> js_rsrcdir = new ArrayList<File>(); List<String> js_repos = new ArrayList<String>(); String js_verbose = null; String js_outRepo = null; IProject project = javaProject.getProject(); CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); final ProgressMonitor$impl<IProgressMonitor>.Progress progress = monitor.Progress$new$(370, ceylon.language.String.instance( "Generating binaries for project " + project.getName())); try { progress.subTask("Preparing binary generation..."); String srcPath = ""; for (IFolder sourceFolder : getSourceFolders(project)) { File sourcePathElement = sourceFolder.getRawLocation().toFile(); if (!srcPath.isEmpty()) { srcPath += File.pathSeparator; } srcPath += sourcePathElement.getAbsolutePath(); js_srcdir.add(sourcePathElement); } options.add("-cwd"); options.add(project.getLocation().toFile().getAbsolutePath()); options.add("-src"); options.add(srcPath); String resPath = ""; for (IFolder resourceFolder : getResourceFolders(project)) { File resourcePathElement = resourceFolder.getRawLocation().toFile(); if (!resPath.isEmpty()) { resPath += File.pathSeparator; } resPath += resourcePathElement.getAbsolutePath(); js_rsrcdir.add(resourcePathElement); } options.add("-res"); options.add(resPath); options.add("-encoding"); options.add(project.getDefaultCharset()); for (String repository : getUserRepositories(project)) { options.add("-rep"); options.add(repository); js_repos.add(repository); } EnumSet<Warning> suppressedWarnings = getSuppressedWarnings(project); if (suppressedWarnings!=null && !suppressedWarnings.isEmpty()) { options.add("-suppress-warnings"); options.add(suppressedWarnings.toString() .replace("[", "").replace("]", "")); } String verbose = System.getProperty("ceylon.verbose"); if (verbose!=null && "true".equals(verbose)) { options.add("-verbose"); js_verbose = "all"; } else { verbose = getVerbose(project); if (verbose!=null) { options.add("-verbose:"+verbose); js_verbose = verbose; } } options.add("-g:lines,vars,source"); String systemRepo = getInterpolatedCeylonSystemRepo(project); if(systemRepo != null && !systemRepo.isEmpty()){ options.add("-sysrep"); options.add(systemRepo); } CeylonProjectConfig config = ceylonProject.getConfiguration(); CeylonIdeConfig ideConfig = ceylonProject.getIdeConfiguration(); String overrides = toJavaString(config.getOverrides()); if (overrides != null) { options.add("-overrides"); options.add(overrides); } String jdkProvider = toJavaString(config.getJdkProvider()); if (jdkProvider != null) { options.add("-jdk-provider"); options.add(jdkProvider); } if (config.getFlatClasspath()) { options.add("-flat-classpath"); } if (config.getAutoExportMavenDependencies()) { options.add("-auto-export-maven-dependencies"); } if (config.getFullyExportMavenDependencies()) { options.add("-fully-export-maven-dependencies"); } final File modulesOutputDir = getCeylonModulesOutputDirectory(project); if (modulesOutputDir!=null) { options.add("-out"); options.add(modulesOutputDir.getAbsolutePath()); js_outRepo = modulesOutputDir.getAbsolutePath(); } CeylonConfig ceylonConfig = config.getCeylonConfig(); if (DefaultToolOptions.getCompilerNoOsgi(ceylonConfig)) { options.add("-noosgi"); } String providedBundles = DefaultToolOptions.getCompilerOsgiProvidedBundles(ceylonConfig); if (providedBundles != null && ! providedBundles.isEmpty()) { options.add("-osgi-provided-bundles"); options.add(providedBundles); } if (DefaultToolOptions.getCompilerNoPom(ceylonConfig)) { options.add("-nopom"); } if (DefaultToolOptions.getCompilerPack200()) { options.add("-pack200"); } String resourceRoot = DefaultToolOptions.getCompilerResourceRootName(ceylonConfig); if (resourceRoot != null) { options.add("-resroot"); options.add(resourceRoot); } String[] aptModules = DefaultToolOptions.getCompilerAptModules(ceylonConfig); if(aptModules != null){ for(String mod : aptModules){ options.add("-apt"); options.add(mod.toString()); } } if (DefaultToolOptions.getCompilerGenerateModuleInfo()) { options.add("-module-info"); } Long targetVersion = DefaultToolOptions.getCompilerTargetVersion(ceylonConfig); if (targetVersion != null) { options.add("-source"); options.add(targetVersion.toString()); options.add("-target"); options.add(targetVersion.toString()); } if (DefaultToolOptions.getCompilerEe(ceylonConfig)) { options.add("-ee"); } List<String> eeImports = DefaultToolOptions.getCompilerEeImport(ceylonConfig); if (eeImports != null) { for (String eeImport: eeImports) { options.add("-ee-import"); options.add(eeImport); } } List<String> eeAnnotations = DefaultToolOptions.getCompilerEeAnnotation(ceylonConfig); if (eeAnnotations != null) { for (String eeAnnotation: eeAnnotations) { options.add("-ee-annotation"); options.add(eeAnnotation); } } List<String> javacOptions = CeylonHelper.toJavaStringList(config.getJavacOptions()); if (javacOptions != null) { CeylonCompileTool.addJavacArguments(options, javacOptions); } progress.worked(20); List<File> forJavaBackend = new ArrayList<File>(); List<File> forJavascriptBackend = new ArrayList<File>(); List<File> javaResources = new ArrayList<File>(); List<File> javaScriptResources = new ArrayList<File>(); ProgressMonitor$impl<IProgressMonitor>.Progress scanFilesProgress = progress.newChild(20).Progress$new$(filesToCompile.size(), null); try { for (IFile file : filesToCompile) { Module module = getModule(file); Backends nativeBackends = null; if (module != null) { nativeBackends = module.getNativeBackends(); } if (nativeBackends.none() || nativeBackends.supports(Backend.Java.asSet())) { if (isInSourceFolder(file)) { if(isCeylon(file) || isJava(file)) { forJavaBackend.add(file.getLocation().toFile()); } } if (isResourceFile(file)) { javaResources.add(file.getLocation().toFile()); } } scanFilesProgress.worked(1); } } finally { scanFilesProgress.destroy(null); } // For the moment the JSCompiler doesn't support partial compilation of a module // so we add all the files to the source files list. // TODO : When JS partial module compilation is supported, re-integrate these lines // in the loop above if (compileToJs(project)) { scanFilesProgress = progress.newChild(30).Progress$new$(ceylonProject.getProjectNativeFiles().getSize(), null); for (IFile file : list(IFile.class, ceylonProject.getProjectNativeFiles())) { Module module = getModule(file); Backends nativeBackends = null; if (module != null) { nativeBackends = module.getNativeBackends(); } if (nativeBackends.none() || nativeBackends.supports(Backend.JavaScript.asSet())) { if (isInSourceFolder(file)) { if(isCeylon(file) || isJavascript(file)) { forJavascriptBackend.add(file.getLocation().toFile()); } } if (isResourceFile(file)) { javaScriptResources.add(file.getLocation().toFile()); } } scanFilesProgress.worked(1); } } progress.updateRemainingWork(300); PrintWriter printWriter = new PrintWriter(verbose==null ? System.out : getConsoleStream(), true); boolean success = true; //Compile JS first if ((forJavascriptBackend.size() + javaScriptResources.size() > 0) && compileToJs(project)) { progress.subTask("Javascript Backend Generation"); success = compileJs(ceylonProject, typeChecker, js_srcdir, js_rsrcdir, js_repos, js_verbose, js_outRepo, printWriter, /*!compileToJava(project) uncomment isntead of true when https://github.com/ceylon/ceylon-compiler/issues/2175 is fixed */true, forJavascriptBackend, javaScriptResources); progress.worked(70); } progress.updateRemainingWork(230); if ((forJavaBackend.size() + javaResources.size() > 0) && compileToJava(project)) { // For Java don't stop compiling when encountering errors options.add("-continue"); // always add the java files, otherwise ceylon code won't see them // and they won't end up in the archives (src/car) progress.subTask("Java Backend Generation"); success = success & compile(ceylonProject, javaProject, options, unitsTypecheckedIncrementally, forJavaBackend, javaResources, typeChecker, printWriter, progress.newChild(200)); } progress.updateRemainingWork(30); if (! compileToJs(project) && /*! compileToJava(project) &&*/ // TODO : uncomment when https://github.com/ceylon/ceylon-compiler/issues/2175 is fixed modulesOutputDir != null) { progress.subTask("Source Archives Generation"); EclipseLogger logger = new EclipseLogger(); RepositoryManager outRepo = repoManager() .offline(config.getOffline()) .cwd(project.getLocation().toFile()) .outRepo(js_outRepo) .logger(logger) .buildOutputManager(); ProgressMonitor$impl<IProgressMonitor>.Progress sourceGenerationMonitor = progress.newChild(30).Progress$new$(getProjectDeclaredSourceModules(project).size(), null); try { for (Module m : getProjectDeclaredSourceModules(project)) { if (m instanceof BaseIdeModule) { ArtifactCreator sac; try { sac = CeylonUtils.makeSourceArtifactCreator(outRepo, js_srcdir, m.getNameAsString(), m.getVersion(), js_verbose!=null, logger); List<String> moduleFiles = new ArrayList<>(); for (IFile file : filesToCompile) { IContainer container = file.getParent(); if (container instanceof IFolder) { if (isSourceFile(file)) { Module fileModule = getModule(((IFolder)container)); if (m.equals(fileModule)) { moduleFiles.add(file.getLocation().toFile().getPath()); } } } } sac.copy(moduleFiles); } catch (IOException e) { e.printStackTrace(); success = false; } } sourceGenerationMonitor.worked(1); } } finally { sourceGenerationMonitor.destroy(null); } } return success; } finally { progress.destroy(null); } } private boolean compileJs(CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject, TypeChecker typeChecker, List<File> js_srcdir, List<File> js_rsrcdir, List<String> js_repos, String js_verbose, String js_outRepo, PrintWriter printWriter, boolean generateSourceArchive, List<File> sources, List<File> resources) throws CoreException { IProject project = ceylonProject.getIdeArtifact(); CeylonProjectConfig config = ceylonProject.getConfiguration(); Options jsopts = new Options() .cwd(ceylonProject.getRootDirectory()) .outWriter(printWriter) .repos(js_repos) .sourceDirs(js_srcdir) .resourceDirs(js_rsrcdir) .systemRepo(getInterpolatedCeylonSystemRepo(project)) .outRepo(js_outRepo) .optimize(true) .verbose(js_verbose) .generateSourceArchive(generateSourceArchive) .encoding(project.getDefaultCharset()) .offline(config.getOffline()); JsCompiler jsc = new JsCompiler(typeChecker, jsopts) { @Override protected boolean nonCeylonUnit(Unit u) { if (! super.nonCeylonUnit(u)) { return false; } if (u instanceof CeylonBinaryUnit) { CeylonBinaryUnit ceylonBinaryUnit = (CeylonBinaryUnit) u; Module module = u.getPackage().getModule(); if (module != null) { if (module.equals(module.getLanguageModule())) { return false; } } if (ceylonBinaryUnit.getCeylonSourceRelativePath() != null) { return false; } } return true; } public File getFullPath(PhasedUnit pu) { VirtualFile virtualFile = pu.getUnitFile(); if (vfsJ2C().instanceOfIFileVirtualFile(virtualFile)) { return vfsJ2C().getIFileVirtualFile(virtualFile).getNativeResource().getLocation().toFile(); } else { return new File(virtualFile.getPath()); } }; }.stopOnErrors(false); try { jsc.setSourceFiles(sources); jsc.setResourceFiles(resources); if (!jsc.generate()) { CompileErrorReporter errorReporter = null; //Report backend errors boolean failed = false; for (Message e : jsc.getErrors()) { if (e instanceof AnalysisMessage) { if (e instanceof UnexpectedError) { failed = true; } if (errorReporter == null) { errorReporter = new CompileErrorReporter(project); } errorReporter.report(new CeylonCompilationError(project, (AnalysisMessage)e)); } } if (failed) { //System.out.println("Ceylon-JS compiler failed for " + project.getName()); errorReporter.failed(); } return false; } else { //System.out.println("compile ok to js"); return true; } } catch (IOException ex) { ex.printStackTrace(printWriter); return false; } } @SuppressWarnings("deprecation") private boolean compile(final CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject, IJavaProject javaProject, List<String> options, final Collection<PhasedUnit> unitsTypecheckedIncrementally, List<File> sources, List<File> resources, final TypeChecker typeChecker, PrintWriter printWriter, ProgressMonitor<IProgressMonitor> mon) throws VerifyError { final IProject project = ceylonProject.getIdeArtifact(); int numberOfJavaFiles = 0; int numberOfCeylonFiles = 0; for (File file : sources) { if (JavaCore.isJavaLikeFileName(file.getName())) { numberOfJavaFiles ++; } else if (file.getName().endsWith(".ceylon")){ numberOfCeylonFiles ++; } } int numberOfSourceFiles = numberOfCeylonFiles + numberOfJavaFiles; final ProgressMonitor$impl<IProgressMonitor>.Progress progress = mon.Progress$new$( numberOfSourceFiles * 2, ceylon.language.String.instance("Generating binaries for " + numberOfSourceFiles + " source files in project " + project.getName())); try { com.redhat.ceylon.compiler.java.tools.CeyloncTool compiler; try { compiler = new com.redhat.ceylon.compiler.java.tools.CeyloncTool(); } catch (VerifyError e) { System.err.println("ERROR: Cannot run tests! Did you maybe forget to configure the -Xbootclasspath/p: parameter?"); throw e; } CompileErrorReporter errorReporter = new CompileErrorReporter(project); final com.redhat.ceylon.langtools.tools.javac.util.Context context = new com.redhat.ceylon.langtools.tools.javac.util.Context(); context.put(com.redhat.ceylon.langtools.tools.javac.util.Log.outKey, printWriter); context.put(DiagnosticListener.class, errorReporter); CeylonLog.preRegister(context); final Map<RegularFileObject, Set<String>> inputFilesToGenerate = new HashMap<RegularFileObject, Set<String>>(); final TaskListener taskListener = new TaskListener() { @Override public void started(TaskEvent ta) { if (progress.isCancelled()) { throw new RuntimeException("Cancelled Java Backend compilation"); } if (! ta.getKind().equals(Kind.PARSE) && ! ta.getKind().equals(Kind.ANALYZE)) { return; } String name = ta.getSourceFile().getName(); name = name.substring(name.lastIndexOf("/")+1); if (ta.getKind().equals(Kind.PARSE)) { CompilationUnitTree cut = ta.getCompilationUnit(); if (cut != null && cut instanceof CeylonCompilationUnit) { progress.subTask("transforming " + name); } else { progress.subTask("parsing " + name); } } if (ta.getKind().equals(Kind.ANALYZE)) { progress.subTask("generating bytecode for " + name); } } @Override public void finished(TaskEvent ta) { if (progress.isCancelled()) { throw new RuntimeException("Cancelled Java Backend compilation"); } if (! ta.getKind().equals(Kind.PARSE) && ! ta.getKind().equals(Kind.ANALYZE)) { return; } if (ta.getKind().equals(Kind.PARSE)) { RegularFileObject sourceFile = BuildFileManager.getSourceFile(ta.getSourceFile()); Set<String> expectedClasses = inputFilesToGenerate.get(sourceFile); if (expectedClasses == null) { expectedClasses = new HashSet<String>(); inputFilesToGenerate.put(sourceFile, expectedClasses); } if (ta.getCompilationUnit() instanceof JCCompilationUnit) { JCCompilationUnit cu = (JCCompilationUnit) ta.getCompilationUnit(); for (JCTree def : cu.defs) { if (def instanceof JCClassDecl) { expectedClasses.add(((JCClassDecl) def).name.toString()); } } } if (expectedClasses.isEmpty()) { inputFilesToGenerate.remove(sourceFile); } } progress.worked(1); } }; BuildFileManager fileManager = new BuildFileManager(context, true, null, project, inputFilesToGenerate) { @Override public void setContext(Context context) { super.setContext(context); context.put(TaskListener.class, taskListener); if (reuseEclipseModelInCompilation(project)) { setupJDTModelLoader(ceylonProject, typeChecker, context, unitsTypecheckedIncrementally); } } }; computeCompilerClasspath(project, javaProject, options); List<File> allFiles = new ArrayList<>(sources.size()+ resources.size()); allFiles.addAll(sources); allFiles.addAll(resources); Iterable<? extends JavaFileObject> unitsToCompile = fileManager.getJavaFileObjectsFromFiles(allFiles); CeyloncTaskImpl task = (CeyloncTaskImpl) compiler.getTask(printWriter, fileManager, errorReporter, options, null, unitsToCompile); task.setTaskListener(taskListener); boolean success=false; try { success = task.call(); } catch (Exception e) { e.printStackTrace(printWriter); } if (!success) { errorReporter.failed(task.getExitState()); } fileManager.addUngeneratedErrors(); return success; } finally { progress.destroy(null); } } private void computeCompilerClasspath(IProject project, IJavaProject javaProject, List<String> options) { List<String> classpathElements = new ArrayList<String>(); // Modules projectModules = getProjectModules(project); // ArtifactContext ctx; // if (projectModules != null) { // Module languageModule = projectModules.getLanguageModule(); // ctx = new ArtifactContext(languageModule.getNameAsString(), // languageModule.getVersion()); // } // else { // ctx = new ArtifactContext(LANGUAGE_MODULE_NAME, // TypeChecker.LANGUAGE_MODULE_VERSION); // } // // ctx.setSuffix(ArtifactContext.CAR); // RepositoryManager repositoryManager = getProjectRepositoryManager(project); // if (repositoryManager!=null) { // //try { // File languageModuleArchive = repositoryManager.getArtifact(ctx); // classpathElements.add(languageModuleArchive.getAbsolutePath()); // /*} // catch (Exception e) { // e.printStackTrace(); // }*/ // } addProjectClasspathElements(classpathElements, javaProject); try { for (IProject p: project.getReferencedProjects()) { if(p.isAccessible()){ addProjectClasspathElements(classpathElements, JavaCore.create(p)); } } } catch (CoreException ce) { ce.printStackTrace(); } options.add("-classpath"); // add the compiletime required jars (those used by the language module implicitely) classpathElements.addAll(CeylonPlugin.getCompiletimeRequiredJars()); String classpath = ""; for (String cpElement : classpathElements) { if (! classpath.isEmpty()) { classpath += File.pathSeparator; } classpath += cpElement; } options.add(classpath); } private void setupJDTModelLoader( final CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject, final TypeChecker typeChecker, final com.redhat.ceylon.langtools.tools.javac.util.Context context, final Collection<PhasedUnit> unitsTypecheckedIncrementally) { final BaseIdeModelLoader modelLoader = getModelLoader(typeChecker); context.put(LanguageCompiler.ceylonContextKey, typeChecker.getContext()); context.put(TypeFactory.class, modelLoader.getTypeFactory()); context.put(LanguageCompiler.compilerDelegateKey, new JdtCompilerDelegate(modelLoader, ceylonProject, typeChecker, context, unitsTypecheckedIncrementally)); context.put(TypeFactory.class, modelLoader.getTypeFactory()); context.put(ModelLoaderFactory.class, new ModelLoaderFactory() { @Override public AbstractModelLoader createModelLoader( com.redhat.ceylon.langtools.tools.javac.util.Context context) { return modelLoader; } }); } private void addProjectClasspathElements(List<String> classpathElements, IJavaProject javaProj) { try { AbstractModelLoader modelLoader = CeylonBuilder.getProjectModelLoader(getProject()); if (modelLoader != null) { Module jdkProviderMod = modelLoader.getJdkProviderModule(); if (jdkProviderMod instanceof BaseIdeModule) { File archiveFile = (((BaseIdeModule) jdkProviderMod).getArtifact()); classpathElements.add(archiveFile.getAbsolutePath()); } } List<IClasspathContainer> containers = getCeylonClasspathContainers(javaProj); for (IClasspathContainer container : containers) { for (IClasspathEntry cpEntry : container.getClasspathEntries()) { if (!isInCeylonClassesOutputFolder(cpEntry.getPath())) { classpathElements.add(cpEntry.getPath().toOSString()); } } } File outputDir = toFile(javaProj.getProject(), javaProj.getOutputLocation() .makeRelativeTo(javaProj.getProject().getFullPath())); classpathElements.add(outputDir.getAbsolutePath()); for (IClasspathEntry cpEntry : javaProj.getResolvedClasspath(true)) { if (isInCeylonClassesOutputFolder(cpEntry.getPath())) { classpathElements.add(javaProj.getProject().getLocation().append(cpEntry.getPath().lastSegment()).toOSString()); } } } catch (JavaModelException e1) { e1.printStackTrace(); } } public static String getVerbose(IProject project) { return getBuilderArgs(project).get("verbose"); } public static void setVerbose(IProject project, String verbose) { if (verbose==null) { getBuilderArgs(project).remove(verbose); } else { getBuilderArgs(project).put("verbose", verbose); } } public static boolean isExplodeModulesEnabled(IProject project) { return compileToJava(project) && "false".equals(System.getProperty("ceylon.disableExplodeModules", "false")); } public static boolean areAstAwareIncrementalBuildsEnabled(IProject project) { return CeylonNature.isEnabled(project) && getBuilderArgs(project).get("astAwareIncrementalBuilds")==null; } public static boolean compileWithJDTModel = true; public static boolean reuseEclipseModelInCompilation(IProject project) { return loadDependenciesFromModelLoaderFirst(project) && compileWithJDTModel; } // Keep it false on master until we fix the associated cross-reference and search issues // by correctly managing source to binary links and indexes public static boolean loadBinariesFirst = "true".equals(System.getProperty("ceylon.loadBinariesFirst", "true")); public static boolean loadDependenciesFromModelLoaderFirst(IProject project) { return compileToJava(project) && loadBinariesFirst; } public static boolean compileToJs(IProject project) { return getBuilderArgs(project).get("compileJs")!=null; } public static boolean compileToJava(IProject project) { return CeylonNature.isEnabled(project) && getBuilderArgs(project).get("compileJava")==null; } public static boolean showWarnings(IProject project) { return !getSuppressedWarnings(project).equals(EnumSet.allOf(Warning.class)); } public static EnumSet<Warning> getSuppressedWarnings(IProject project) { if (project!=null) { CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return ceylonProject.getConfiguration().getSuppressWarningsEnum(); } } return EnumSet.noneOf(Warning.class); } public static List<String> getUserRepositories(IProject project) throws CoreException { List<String> ceylonRepos = getCeylonRepositories(project); List<String> userRepos = new ArrayList<>(ceylonRepos); userRepos.addAll(getReferencedProjectsOutputRepositories(project)); return userRepos; } public static List<String> getReferencedProjectsOutputRepositories(IProject project) throws CoreException { List<String> repos = new ArrayList<String>(); if (project != null) { for (IProject referencedProject: project.getReferencedProjects()) { if (referencedProject.isAccessible() && CeylonNature.isEnabled(referencedProject)) { repos.add(getCeylonModulesOutputDirectory(referencedProject).getAbsolutePath()); } } } return repos; } private static Map<String,String> getBuilderArgs(IProject project) { if (project!=null) { try { for (ICommand c: project.getDescription().getBuildSpec()) { if (c.getBuilderName().equals(BUILDER_ID)) { return c.getArguments(); } } } catch (CoreException e) { e.printStackTrace(); } } return Collections.emptyMap(); } public static List<String> getCeylonRepositories(IProject project) { CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel() .getProject(project); if (ceylonProject != null) { return toJavaStringList(ceylonProject.getCeylonRepositories()); } else { return Collections.emptyList(); } } private static File toFile(IProject project, IPath path) { return project.getFolder(path).getRawLocation().toFile(); } private static void clearMarkersOn(IResource resource, boolean alsoDeleteBackendErrors) { clearMarkersOn(resource, alsoDeleteBackendErrors, false); } private static void clearMarkersOn(IResource resource, boolean alsoDeleteBackendErrors, boolean onlyBackendErrors) { try { if (!onlyBackendErrors) { resource.deleteMarkers(TASK_MARKER_ID, false, DEPTH_INFINITE); resource.deleteMarkers(PROBLEM_MARKER_ID, true, DEPTH_INFINITE); } if (alsoDeleteBackendErrors) { resource.deleteMarkers(PROBLEM_MARKER_ID + ".backend", true, DEPTH_INFINITE); for (IMarker javaMarker : resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE)) { if (CeylonPlugin.PLUGIN_ID.equals(javaMarker.getAttribute(IMarker.SOURCE_ID))) { javaMarker.delete(); } } } if (!onlyBackendErrors) { //these are actually errors from the Ceylon compiler, but //we did not bother creating a separate annotation type! resource.deleteMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, true, DEPTH_INFINITE); } } catch (CoreException e) { e.printStackTrace(); } } private static void clearProjectMarkers(IProject project, boolean nonBackendMarkers, boolean backendMarkers) { //project.deleteMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, true, DEPTH_ZERO); if (nonBackendMarkers) { try { project.deleteMarkers(PROBLEM_MARKER_ID, true, DEPTH_ZERO); } catch (CoreException e) { e.printStackTrace(); } } if (backendMarkers) { try { project.deleteMarkers(PROBLEM_MARKER_ID + ".backend", true, DEPTH_ZERO); } catch (CoreException e) { e.printStackTrace(); } } } private static void clearMarkersOn(Collection<IFile> files, boolean alsoDeleteBackendErrors, boolean onlyBackendErrors) { for(IFile file: files) { clearMarkersOn(file, alsoDeleteBackendErrors, onlyBackendErrors); } } private static void clearMarkersOn(Collection<IFile> files, boolean alsoDeleteBackendErrors) { clearMarkersOn(files, alsoDeleteBackendErrors, false); } /*private void dumpSourceList(Collection<IFile> sourcesToCompile) { MessageConsoleStream consoleStream= getConsoleStream(); for(Iterator<IFile> iter= sourcesToCompile.iterator(); iter.hasNext(); ) { IFile srcFile= iter.next(); consoleStream.println(" " + srcFile.getFullPath()); } }*/ private static MessageConsoleStream getConsoleStream() { return findConsole().newMessageStream(); } // // protected static MessageConsoleStream getConsoleErrorStream() { // final MessageConsoleStream stream = findConsole().newMessageStream(); // //TODO: all this, just to get the color red? can that be right?? // /*try { // getWorkbench().getProgressService().runInUI(getWorkbench().getWorkbenchWindows()[0], // new IRunnableWithProgress() { // // @Override // public void run(IProgressMonitor monitor) throws InvocationTargetException, // InterruptedException { // stream.setColor(getWorkbench().getDisplay().getSystemColor(SWT.COLOR_RED)); // } // }, null); // } // catch (Exception e) { // e.printStackTrace(); // }*/ // return stream; // } // // private String timedMessage(String message) { // long elapsedTimeMs = (System.nanoTime() - startTime) / 1000000; // return String.format("[%1$10d] %2$s", elapsedTimeMs, message); // } /** * Find or create the console with the given name, and * bring it to the top * @param consoleName */ protected static MessageConsole findConsole() { MessageConsole myConsole = null; final IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager(); IConsole[] consoles = consoleManager.getConsoles(); for(int i= 0; i < consoles.length; i++) { IConsole console = consoles[i]; if (console.getName().equals(CEYLON_CONSOLE)) myConsole = (MessageConsole) console; } if (myConsole == null) { ImageDescriptor image = CeylonPlugin.imageRegistry() .getDescriptor(CeylonResources.BUILDER); myConsole = new MessageConsole(CEYLON_CONSOLE, image); consoleManager.addConsoles(new IConsole[] { myConsole }); } consoleManager.showConsoleView(myConsole); return myConsole; } private static void addTaskMarkers(IFile file, List<CommonToken> tokens) { // clearTaskMarkersOn(file); for (CommonToken token : tokens) { int tt = token.getType(); if (tt == CeylonLexer.LINE_COMMENT || tt == CeylonLexer.MULTI_COMMENT) { CeylonTaskUtil.addTaskMarkers(token, file); } } } @Override protected void clean(IProgressMonitor monitor) throws CoreException { super.clean(monitor); IProject project = getProject(); // startTime = System.nanoTime(); // getConsoleStream().println("\n==================================="); // getConsoleStream().println(timedMessage("Starting Ceylon clean on project: " + project.getName())); // getConsoleStream().println("-----------------------------------"); cleanupModules(monitor, project); cleanupJdtClasses(monitor, project); monitor.subTask("Clearing project and source markers for project " + project.getName()); clearProjectMarkers(project, true, true); clearMarkersOn(project, true); // getConsoleStream().println("-----------------------------------"); // getConsoleStream().println(timedMessage("End Ceylon clean on project: " + project.getName())); // getConsoleStream().println("==================================="); } private void cleanupJdtClasses(IProgressMonitor monitor, IProject project) { if (isExplodeModulesEnabled(project)) { monitor.subTask("Cleaning exploded modules directory of project " + project.getName()); final File ceylonOutputDirectory = getCeylonClassesOutputDirectory(project); new RepositoryLister(Arrays.asList(".*")) .list(ceylonOutputDirectory, new RepositoryLister.Actions() { @Override public void doWithFile(File path) { path.delete(); } public void exitDirectory(File path) { if (path.list().length == 0 && !path.equals(ceylonOutputDirectory)) { path.delete(); } } }); } } private void cleanupModules(IProgressMonitor monitor, IProject project) { final File modulesOutputDirectory = getCeylonModulesOutputDirectory(project); if (modulesOutputDirectory != null) { monitor.subTask("Cleaning existing artifacts of project " + project.getName()); final List<String> extensionsToDelete = Arrays.asList(".jar", ".js", ".car", ".src", ".sha1"); final List<String> deleteEverything = Arrays.asList(".*"); new RepositoryLister(extensionsToDelete) .list(modulesOutputDirectory, new RepositoryLister.Actions() { @Override public void doWithFile(File path) { if (path.getName().endsWith(ArtifactContext.CAR)) { File moduleResourcesDirectory = new File(path.getParentFile(), ArtifactContext.RESOURCES); if (moduleResourcesDirectory.exists()) { new RepositoryLister(deleteEverything) .list(moduleResourcesDirectory, new RepositoryLister.Actions() { @Override public void doWithFile(File path) { path.delete(); } @Override public void exitDirectory(File path) { if (path.list().length == 0) { path.delete(); } } }); } } path.delete(); } @Override public void exitDirectory(File path) { if (path.list().length == 0 && !path.equals(modulesOutputDirectory)) { path.delete(); } } }); } } // TODO think: doRefresh(file.getParent()); // N.B.: Assumes all // generated files go into parent folder public static RepositoryManager getProjectRepositoryManager(IProject project) { CeylonProject ceylonProject = modelJ2C().ceylonModel() .getProject(project); if (ceylonProject != null) { return ceylonProject.getRepositoryManager(); } return null; } public static TypeChecker getProjectTypeChecker(IProject project) { CeylonProject ceylonProject = modelJ2C().ceylonModel() .getProject(project); if (ceylonProject != null && ceylonProject.getParsed()) { return ceylonProject.getTypechecker(); } return null; } public static PhasedUnits getProjectPhasedUnits(IProject project) { TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker != null) { return typeChecker.getPhasedUnits(); } return null; } public static Modules getProjectModules(IProject project) { TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker == null) { return null; } return typeChecker.getContext() .getModules(); } public static Collection<BaseIdeModule> getProjectExternalModules(IProject project) { TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker == null) { return Collections.emptyList(); } List<BaseIdeModule> modules = new ArrayList<>(); for (Module m : typeChecker.getContext() .getModules() .getListOfModules()) { if (m instanceof BaseIdeModule) { BaseIdeModule module = (BaseIdeModule) m; if (! module.getIsProjectModule()) { modules.add(module); } } } return modules; } public static Collection<Module> getProjectSourceModules(IProject project) { List<Module> moduleList = new ArrayList<Module>(); moduleList.addAll(getProjectDeclaredSourceModules(project)); Modules projectModules = getProjectModules(project); if (projectModules != null) { moduleList.add(projectModules.getDefaultModule()); } return moduleList; } public static Collection<Module> getProjectDeclaredSourceModules(IProject project) { TypeChecker typeChecker = getProjectTypeChecker(project); if (typeChecker == null) { return Collections.emptyList(); } List<Module> modules = new ArrayList<>(); for (Module m : typeChecker.getPhasedUnits() .getModuleSourceMapper() .getCompiledModules()) { if (m instanceof BaseIdeModule) { BaseIdeModule module = (BaseIdeModule) m; if (module.getIsProjectModule()) { modules.add(module); } } } return modules; } public static void removeOverridesProblemMarker(final IProject project) { Job job = new Job("Remove Overrides problem marker") { @Override protected IStatus run(IProgressMonitor monitor) { try { IResource lastOverridesWithProblem = findLastOverridesProblemMarker(project); if (lastOverridesWithProblem != null) { if (lastOverridesWithProblem.findMarkers( CEYLON_INVALID_OVERRIDES_MARKER, false, DEPTH_ZERO).length > 0) { lastOverridesWithProblem.deleteMarkers( CEYLON_INVALID_OVERRIDES_MARKER, false, DEPTH_ZERO); project.setPersistentProperty( new QualifiedName(CeylonPlugin.PLUGIN_ID, "lastOverridesProblemMarker"), null); } } } catch (CoreException e) { e.printStackTrace(); } return Status.OK_STATUS; } }; job.setRule(project); job.schedule(); } public static IFile fileToIFile(File file, IProject project) { IPath projectLocation = project.getLocation(); IPath absolutePath = new Path(file.getAbsolutePath()); if (projectLocation.isPrefixOf(absolutePath)) { IPath projectRelativePath = absolutePath.removeFirstSegments( projectLocation.segmentCount()); IResource resource = project.findMember(projectRelativePath); if (resource != null && resource.isAccessible() && resource instanceof IFile) { return (IFile) resource; } } return null; } private static IResource findLastOverridesProblemMarker(IProject project) { try { String projectRelativePath = project.getPersistentProperty( new QualifiedName(CeylonPlugin.PLUGIN_ID, "lastOverridesProblemMarker")); if (projectRelativePath != null) { IResource resource = project.findMember(projectRelativePath); if (resource != null && resource.isAccessible()) { return resource; } } } catch(CoreException e) { } return null; } public static void createOverridesProblemMarker(final IProject project, final Exception e, final File overridesFile, final int line, final int column) { Job job = new Job("Create Overrides problem marker") { @Override protected IStatus run(IProgressMonitor monitor) { try { IResource markerResource = project; IFile overridesResource = fileToIFile(overridesFile, project); if (overridesResource != null) { markerResource = overridesResource; } IResource lastOverridesWithProblem = findLastOverridesProblemMarker(project); if (lastOverridesWithProblem != null) { lastOverridesWithProblem.deleteMarkers( CEYLON_INVALID_OVERRIDES_MARKER, false, DEPTH_ZERO); } project.setPersistentProperty( new QualifiedName(CeylonPlugin.PLUGIN_ID, "lastOverridesProblemMarker"), markerResource.getProjectRelativePath().toString()); IMarker marker = markerResource.createMarker(CEYLON_INVALID_OVERRIDES_MARKER); marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); if (line > -1) { marker.setAttribute(IMarker.LOCATION, "Line " + line); marker.setAttribute(IMarker.LINE_NUMBER, line); if (column > -1 && markerResource instanceof IFile) { TextFileDocumentProvider docProvider = new TextFileDocumentProvider(); docProvider.connect(markerResource); IDocument overridesDocument = docProvider.getDocument(markerResource); if (overridesDocument != null) { IRegion lineInfo; try { lineInfo = overridesDocument.getLineInformation(line-1); int endCharOffset = lineInfo.getOffset() + column - 1; if (endCharOffset == lineInfo.getOffset() + lineInfo.getLength()) { while (endCharOffset > lineInfo.getOffset()) { char lineEnd = overridesDocument.getChar(endCharOffset); if (lineEnd == '\n' || lineEnd == '\r') { endCharOffset --; } else { break; } } } char endChar = overridesDocument.getChar(endCharOffset); int firstCharOffset = endCharOffset - 2; if (firstCharOffset < 0) { firstCharOffset= 0; } if (endChar == '>') { int offset = endCharOffset -1; while (offset >= lineInfo.getOffset()) { if (overridesDocument.getChar(offset) == '<') { firstCharOffset = offset; break; } offset--; } } marker.setAttribute(IMarker.CHAR_START, firstCharOffset); marker.setAttribute(IMarker.CHAR_END, endCharOffset +1); } catch (BadLocationException e1) { } } } } marker.setAttribute(IMarker.SOURCE_ID, CeylonBuilder.SOURCE); marker.setAttribute(IMarker.MESSAGE, "The Module Resolver Overrides file is invalid : " + e.getMessage()); } catch (CoreException e) { e.printStackTrace(); } return Status.OK_STATUS; } }; job.setRule(project); job.schedule(); } // public static RepositoryManager createProjectRepositoryManager(final IProject project) throws CoreException { // modelJ2C().ceylonModel().addProject(project); // CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); // // RepositoryManager repositoryManager = new CeylonUtils.CeylonRepoManagerBuilder() { // protected com.redhat.ceylon.cmr.api.Overrides getOverrides(String path) { // if (path == null) { // removeOverridesProblemMarker(project); // } // return super.getOverrides(path); // } // protected com.redhat.ceylon.cmr.api.Overrides getOverrides(File absoluteFile) { // Overrides result = null; // Exception overridesException = null; // int overridesLine = -1; // int overridesColumn = -1; // try { // result = super.getOverrides(absoluteFile); // } catch(Overrides.InvalidOverrideException e) { // overridesException = e; // overridesLine = e.line; // overridesColumn = e.column; // } catch(IllegalStateException e) { // Throwable cause = e.getCause(); // if (cause instanceof SAXParseException) { // SAXParseException parseException = (SAXParseException) cause; // overridesException = parseException; // overridesLine = parseException.getLineNumber(); // overridesColumn = parseException.getColumnNumber(); // } else if (cause instanceof Exception) { // overridesException = (Exception) cause; // } else { // overridesException = e; // } // } catch(Exception e) { // overridesException = e; // } // // if (overridesException != null) { // createOverridesProblemMarker( // project, // overridesException, // absoluteFile, // overridesLine, // overridesColumn); // } else { // removeOverridesProblemMarker(project); // } // return result; // }; // // } // .offline(ceylonProject.getConfiguration().getOffline()) // .cwd(project.getLocation().toFile()) // .systemRepo(getInterpolatedCeylonSystemRepo(project)) // .extraUserRepos(getReferencedProjectsOutputRepositories(project)) // .logger(new EclipseLogger()) // .isJDKIncluded(true) // .buildManager(); // // return repositoryManager; // } public static Collection<CeylonProject<IProject, IResource, IFolder, IFile>> getCeylonProjects() { return modelJ2C().ceylonModel() .getCeylonProjectsAsJavaList(); } public static Collection<IProject> getProjects() { return modelJ2C().ceylonModel() .getNativeProjectsAsJavaList(); } public static Collection<TypeChecker> getTypeCheckers() { Collection<CeylonProject<IProject, IResource, IFolder, IFile>> ceylonProjects = getCeylonProjects(); ArrayList<TypeChecker> typeCheckers = new ArrayList<>(ceylonProjects.size()); for (CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject : ceylonProjects) { TypeChecker tc = ceylonProject.getTypechecker(); if (tc != null) { typeCheckers.add(tc); } } return typeCheckers; } public static void removeProject(IProject project) { containersInitialized.remove(project); JavaProjectStateMirror.cleanup(project); } public static List<IFolder> getSourceFolders(IProject project) { CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return list(IFolder.class, ceylonProject.getSourceNativeFolders()); } return Collections.emptyList(); } public static List<IFolder> getResourceFolders(IProject project) { CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return list(IFolder.class, ceylonProject.getResourceNativeFolders()); } return Collections.emptyList(); } // public static List<IFolder> getSourceFolders(IProject project) { // //TODO: is the call to JavaCore.create() very expensive?? // List<IPath> folderPaths = getSourceFolders(JavaCore.create(project)); // List<IFolder> sourceFolders = new ArrayList<>(folderPaths.size()); // for (IPath path : folderPaths) { // IResource r = project.findMember(path.makeRelativeTo(project.getFullPath())); // if (r instanceof IFolder) { // sourceFolders.add((IFolder) r); // } // } // return sourceFolders; // } // // /** // * Read the IJavaProject classpath configuration and populate the ISourceProject's // * build path accordingly. // */ // public static List<IPath> getSourceFolders(IJavaProject javaProject) { // if (javaProject.exists()) { // try { // List<IPath> projectSourceFolders = new ArrayList<IPath>(); // for (IClasspathEntry entry: javaProject.getRawClasspath()) { // IPath path = entry.getPath(); // if (isCeylonSourceEntry(entry)) { // projectSourceFolders.add(path); // } // } // return projectSourceFolders; // } // catch (JavaModelException e) { // e.printStackTrace(); // } // } // return Collections.emptyList(); // } // // public static List<IFolder> getResourceFolders(IProject project) { // LinkedList<IFolder> resourceFolers = new LinkedList<>(); // if (project.exists()) { // CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); // if (ceylonProject != null) { // for (String resourceInConfig : toJavaStringList(ceylonProject.getConfiguration().getResourceDirectories())) { // class FolderHolder { // IFolder resourceFolder; // } // final FolderHolder folderHolder = new FolderHolder();; // final IPath path = Path.fromOSString(resourceInConfig); // if (! path.isAbsolute()) { // folderHolder.resourceFolder = project.getFolder(path); // } else { // try { // project.accept(new IResourceVisitor() { // @Override // public boolean visit(IResource resource) // throws CoreException { // if (resource instanceof IFolder && // resource.isLinked() && // resource.getLocation() != null && // resource.getLocation().equals(path)) { // folderHolder.resourceFolder = (IFolder) resource; // return false; // } // return resource instanceof IFolder || // resource instanceof IProject; // } // }); // } // catch (CoreException e) { // e.printStackTrace(); // } // } // if (folderHolder.resourceFolder != null && // folderHolder.resourceFolder.exists()) { // resourceFolers.add(folderHolder.resourceFolder); // } // } // } // } // return resourceFolers; // } // // public static List<IFolder> getRootFolders(IProject project) { // LinkedList<IFolder> rootFolders = new LinkedList<>(); // rootFolders.addAll(getSourceFolders(project)); // rootFolders.addAll(getResourceFolders(project)); // return rootFolders; // } // // public static boolean isCeylonSourceEntry(IClasspathEntry entry) { // if (entry.getEntryKind()!=IClasspathEntry.CPE_SOURCE) { // return false; // } // // for (IPath exclusionPattern : entry.getExclusionPatterns()) { // if (exclusionPattern.toString().endsWith(".ceylon")) { // return false; // } // } // // return true; // } public static IFolder getRootFolder(IFolder folder) { FolderVirtualFile<IProject, IResource, IFolder, IFile> rootVirtualFile = vfsJ2C().createVirtualFolder(folder, folder.getProject()).getRootFolder(); if (rootVirtualFile == null) { return null; } return rootVirtualFile.getNativeResource(); } public static RootFolderType getRootFolderType(IPackageFragmentRoot pfr) { IResource resource = null; try { resource = pfr.getCorrespondingResource(); } catch (JavaModelException e) { } if (resource instanceof IFolder) { return getRootFolderType((IFolder) resource); } return null; } public static boolean isSourceFolder(IPackageFragmentRoot pfr) { return RootFolderType.SOURCE.equals(getRootFolderType(pfr)); } public static boolean isResourceFolder(IPackageFragmentRoot pfr) { return RootFolderType.RESOURCE.equals(getRootFolderType(pfr)); } public static boolean isInSourceFolder(IPackageFragment pf) { return RootFolderType.SOURCE.equals(getRootFolderType(pf)); } public static boolean isInResourceFolder(IPackageFragment pf) { return RootFolderType.RESOURCE.equals(getRootFolderType(pf)); } public static RootFolderType getRootFolderType(IPackageFragment pf) { IResource resource = null; try { resource = pf.getCorrespondingResource(); } catch (JavaModelException e) { } if (resource instanceof IFolder) { return getRootFolderType((IFolder) resource); } return null; } public static IFolder getRootFolder(IFile file) { if (file.getParent() instanceof IFolder) { return getRootFolder((IFolder) file.getParent()); } return null; } public static RootFolderType getRootFolderType(IFolder folder) { ceylon.language.Boolean isSourceFolder = vfsJ2C().createVirtualFolder(folder, folder.getProject()) .getIsSource(); if (isSourceFolder == null) { return null; } return isSourceFolder.booleanValue() ? RootFolderType.SOURCE : RootFolderType.RESOURCE; } public static RootFolderType getRootFolderType(IFile file) { ceylon.language.Boolean isSourceFolder = vfsJ2C().createVirtualFile(file, file.getProject()) .getIsSource(); if (isSourceFolder == null) { return null; } return isSourceFolder.booleanValue() ? RootFolderType.SOURCE : RootFolderType.RESOURCE; } public static boolean isInSourceFolder(IFile file) { return getRootFolderType(file) == RootFolderType.SOURCE; } public static String getPackageName(IResource resource) { if (resource instanceof IFolder) { return getPackage((IFolder) resource).getQualifiedNameString(); } if (resource instanceof IFile) { return getPackage((IFile) resource).getQualifiedNameString(); } return null; } public static Package getPackage(IFolder resource) { ResourceVirtualFile<IProject, IResource, IFolder, IFile> resourceVirtualFile = vfsJ2C().createVirtualResource(resource, resource.getProject()); return resourceVirtualFile.getCeylonPackage(); } public static Package getPackage(IFile file) { ResourceVirtualFile<IProject, IResource, IFolder, IFile> resourceVirtualFile = vfsJ2C().createVirtualResource(file, file.getProject()); return resourceVirtualFile.getCeylonPackage(); } @SuppressWarnings("unchecked") public static Package getPackage(VirtualFile virtualFile) { if (virtualFile instanceof FileVirtualFile) { FileVirtualFile<IProject, IResource, IFolder, IFile> fvf = (FileVirtualFile<IProject, IResource, IFolder, IFile>) virtualFile; return fvf.getCeylonPackage(); } if (virtualFile instanceof FolderVirtualFile) { FolderVirtualFile<IProject, IResource, IFolder, IFile> fvf = (FolderVirtualFile<IProject, IResource, IFolder, IFile>) virtualFile; return fvf.getCeylonPackage(); } String virtualPath = virtualFile.getPath(); if (virtualPath.contains("!/")) { // TODO : this test could be replaced by an instanceof if the ZipEntryVirtualFile was public Unit ceylonUnit = getUnit(virtualFile); if (ceylonUnit != null) { return ceylonUnit.getPackage(); } } return null; } public static CeylonUnit getUnit(VirtualFile virtualFile) { if (vfsJ2C().instanceOfIFileVirtualFile(virtualFile)) { IFile file = vfsJ2C() .getIFileVirtualFile(virtualFile) .getNativeResource(); Package p = getPackage(file); if (p != null) { for (Unit u : p.getUnits()) { if (u instanceof SourceFile && u.getFilename() .equals(file.getName())) { return (SourceFile) u; } } } return null; } String virtualPath = virtualFile.getPath(); if (virtualPath.contains("!/")) { // TODO : this test could be replaced by an instanceof if the ZipEntryVirtualFile was public for (IProject p : getProjects()) { BaseIdeModuleManager moduleManager = getProjectModuleManager(p); if (moduleManager != null) { BaseIdeModule archiveModule = moduleManager.getArchiveModuleFromSourcePath( ceylon.language.String.instance(virtualPath)); if (archiveModule != null) { ExternalPhasedUnit pu = archiveModule.getPhasedUnit(virtualFile); if (pu != null) { return pu.getUnit(); } } } } } return null; } @SuppressWarnings("unchecked") public static IResourceAware<IProject,IFolder,IFile> getUnit(IFile file) { Package p = getPackage(file); if (p != null) { for (Unit u: p.getUnits()) { if (u instanceof IResourceAware) { if (u.getFilename().equals(file.getName())) { return (IResourceAware<IProject,IFolder,IFile>) u; } } } } return null; } public static Package getPackage(IPackageFragment packageFragment) { try { IFolder srcPkgFolder = (IFolder) packageFragment.getCorrespondingResource(); if (srcPkgFolder != null) { return getPackage(srcPkgFolder); } } catch (JavaModelException e) { } IPackageFragmentRoot root = (IPackageFragmentRoot) packageFragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); IProject project = packageFragment.getJavaProject() .getProject(); Modules projectModules = getProjectModules(project); if (projectModules == null) { return null; } for (Module m : projectModules.getListOfModules()) { if (m instanceof BaseIdeModule && !m.getNameAsString() .equals(Module.DEFAULT_MODULE_NAME)) { BaseIdeModule module = (BaseIdeModule) m; for (IPackageFragmentRoot moduleRoot : modelJ2C().getModulePackageFragmentRoots(module)) { if (root.getPath().equals(moduleRoot.getPath())) { Package result = module.getDirectPackage( packageFragment.getElementName()); if (result != null) { return result; } } } } } BaseIdeModule defaultModule = (BaseIdeModule) projectModules.getDefaultModule(); for (IPackageFragmentRoot moduleRoot : modelJ2C().getModulePackageFragmentRoots(defaultModule)) { if (root.getPath().equals(moduleRoot.getPath())) { Package result = defaultModule.getDirectPackage( packageFragment.getElementName()); if (result != null) { return result; } } } return null; } public static BaseIdeModule asSourceModule(IFolder moduleFolder) { Package p = getPackage(moduleFolder); if (p != null) { Module m = p.getModule(); if (m instanceof BaseIdeModule && m.getNameAsString() .equals(p.getNameAsString())) { return (BaseIdeModule) m; } } return null; } public static BaseIdeModule asSourceModule(IPackageFragment sourceModuleFragment) { IFolder moduleFolder; try { moduleFolder = (IFolder) sourceModuleFragment.getCorrespondingResource(); if (moduleFolder != null) { return asSourceModule(moduleFolder); } } catch (JavaModelException e) { } return null; } public static BaseIdeModule getModule(IFile file) { Package p = getPackage(file); if (p != null) { Module m = p.getModule(); if (m instanceof BaseIdeModule) { return (BaseIdeModule) m; } } return null; } public static BaseIdeModule getModule(IFolder moduleFolder) { Package p = getPackage(moduleFolder); if (p != null) { Module m = p.getModule(); if (m instanceof BaseIdeModule) { return (BaseIdeModule) m; } } return null; } public static BaseIdeModule getModule(IPackageFragment packageFragment) { Package p = getPackage(packageFragment); if (p != null) { Module m = p.getModule(); if (m instanceof BaseIdeModule) { return (BaseIdeModule) m; } } return null; } @SuppressWarnings("unchecked") public static IJavaModelAware<IProject,ITypeRoot,IJavaElement> getUnit(IJavaElement javaElement) { IOpenable openable = javaElement.getOpenable(); if (openable instanceof ITypeRoot) { ITypeRoot typeRoot = (ITypeRoot) openable; IPackageFragment pf = (IPackageFragment) typeRoot.getParent(); Package p = getPackage(pf); if (p != null) { String className = typeRoot.getElementName(); if (className.equals(Naming.PACKAGE_DESCRIPTOR_CLASS_NAME+".class") || className.equals(Naming.PACKAGE_DESCRIPTOR_CLASS_NAME.substring(1)+".class")) { Unit packageUnit = p.getUnit(); if (packageUnit instanceof IJavaModelAware) { IJavaModelAware<IProject, ITypeRoot, IJavaElement> jma = (IJavaModelAware<IProject, ITypeRoot, IJavaElement>) packageUnit; if (jma.getTypeRoot().equals(openable)) { return jma; } } } if (className.equals(Naming.MODULE_DESCRIPTOR_CLASS_NAME+".class") || className.equals(Naming.OLD_MODULE_DESCRIPTOR_CLASS_NAME+".class")) { Unit moduleUnit = p.getModule().getUnit(); if (moduleUnit instanceof IJavaModelAware) { IJavaModelAware<IProject, ITypeRoot, IJavaElement> jma = (IJavaModelAware<IProject, ITypeRoot, IJavaElement>) moduleUnit; if (jma.getTypeRoot().equals(openable)) { return jma; } } } for (Declaration d : p.getMembers()) { Unit u = d.getUnit(); if (u instanceof IJavaModelAware) { IJavaModelAware<IProject, ITypeRoot, IJavaElement> jma = (IJavaModelAware<IProject, ITypeRoot, IJavaElement>) u; if (u.getFilename().equals(typeRoot.getElementName())) { return jma; } } } } } return null; } private void cleanRemovedFilesFromOutputs( Collection<IFile> filesToRemove, CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject) { if (filesToRemove.size() == 0) { return; } IProject project = ceylonProject.getIdeArtifact(); Set<File> moduleJars = new HashSet<File>(); for (IFile file : filesToRemove) { IFolder rootFolder = getRootFolder(file); if (rootFolder == null) { return; } IPath projectRelativePath = rootFolder.getProjectRelativePath(); String relativeFilePath = file.getProjectRelativePath() .makeRelativeTo(projectRelativePath) .toString(); IFolder folder = (IFolder) file.getParent(); Package pkg = getPackage(folder); if (pkg == null) { return; } Module module = pkg.getModule(); TypeChecker typeChecker = ceylonProject.getTypechecker(); if (typeChecker == null) { return; } final File modulesOutputDirectory = getCeylonModulesOutputDirectory(project); boolean explodeModules = isExplodeModulesEnabled(project); final File ceylonOutputDirectory = explodeModules ? getCeylonClassesOutputDirectory(project) : null; File moduleDir = getModulePath(modulesOutputDirectory, module); boolean fileIsResource = isResourceFile(file); //Remove the classes belonging to the source file from the //module archive and from the JDTClasses directory File moduleJar = new File(moduleDir, getModuleArchiveName(module)); if(moduleJar.exists()){ moduleJars.add(moduleJar); try { List<String> entriesToDelete = new ArrayList<String>(); ZipFile zipFile = new ZipFile(moduleJar); Properties mapping = CarUtils.retrieveMappingFile(zipFile); if (fileIsResource) { entriesToDelete.add(relativeFilePath); } else { for (String className : mapping.stringPropertyNames()) { String sourceFile = mapping.getProperty(className); if (relativeFilePath.equals(sourceFile)) { entriesToDelete.add(className); } } } for (String entryToDelete : entriesToDelete) { try { zipFile.removeFile(entryToDelete); } catch (ZipException e) { } if (explodeModules) { new File(ceylonOutputDirectory, entryToDelete.replace('/', File.separatorChar)) .delete(); } } } catch (ZipException e) { e.printStackTrace(); } } if (!fileIsResource) { //Remove the source file from the source archive File moduleSrc = new File(moduleDir, getSourceArchiveName(module)); if(moduleSrc.exists()){ moduleJars.add(moduleSrc); try { ZipFile zipFile = new ZipFile(moduleSrc); FileHeader fileHeader = zipFile.getFileHeader(relativeFilePath); if(fileHeader != null){ zipFile.removeFile(fileHeader); } } catch (ZipException e) { e.printStackTrace(); } } } if (fileIsResource) { File resourceFile = new File( moduleDir, "module-resources" + File.separator + relativeFilePath.replace('/', File.separatorChar)); resourceFile.delete(); } } // final com.sun.tools.javac.util.Context dummyContext = new com.sun.tools.javac.util.Context(); class ConsoleLog implements Logger { PrintWriter writer; ConsoleLog() { writer = new PrintWriter(System.out); //new PrintWriter(getConsoleStream())); } @Override public void error(String str) { writer.println("Error: " + str); } @Override public void warning(String str) { writer.println("Warning: " + str); } @Override public void info(String str) { } @Override public void debug(String str) { } } ConsoleLog log = new ConsoleLog(); for (File moduleJar: moduleJars) { ShaSigner.sign(moduleJar, log, false); } } private void cleanChangedFilesFromExplodedDirectory( Collection<IFile> changedFiles, CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject) { if (changedFiles.size() == 0) { return; } IProject project = ceylonProject.getIdeArtifact(); if (! isExplodeModulesEnabled(project)) { return; } for (IFile file : changedFiles) { IFolder rootFolder = getRootFolder(file); if (rootFolder == null) { return; } if (isResourceFile(file)) { return; } IPath projectRelativePath = rootFolder.getProjectRelativePath(); String relativeFilePath = file.getProjectRelativePath() .makeRelativeTo(projectRelativePath) .toString(); Package pkg = getPackage((IFolder)file.getParent()); if (pkg == null) { return; } Module module = pkg.getModule(); TypeChecker typeChecker = ceylonProject.getTypechecker(); if (typeChecker == null) { return; } final File modulesOutputDirectory = getCeylonModulesOutputDirectory(project); final File ceylonOutputDirectory = getCeylonClassesOutputDirectory(project); File moduleDir = getModulePath(modulesOutputDirectory, module); //Remove the classes belonging to the source file from the //from the .exploded directory File moduleJar = new File(moduleDir, getModuleArchiveName(module)); if(moduleJar.exists()) { try { List<String> entriesToDelete = new ArrayList<String>(); ZipFile zipFile = new ZipFile(moduleJar); Properties mapping = CarUtils.retrieveMappingFile(zipFile); for (String className : mapping.stringPropertyNames()) { String sourceFile = mapping.getProperty(className); if (relativeFilePath.equals(sourceFile)) { entriesToDelete.add(className); } } for (String entryToDelete : entriesToDelete) { new File(ceylonOutputDirectory, entryToDelete.replace('/', File.separatorChar)) .delete(); } } catch (ZipException e) { e.printStackTrace(); } } } } public static File getCeylonClassesOutputDirectory(IProject project) { return getCeylonClassesOutputFolder(project) .getRawLocation().toFile(); } public static IFolder getCeylonClassesOutputFolder(IProject project) { return project.getFolder(CEYLON_CLASSES_FOLDER_NAME); } public static boolean isInCeylonClassesOutputFolder(IPath path) { //TODO: this is crap! return path.lastSegment().equals(CEYLON_CLASSES_FOLDER_NAME); } public static File getCeylonModulesOutputDirectory(IProject project) { return getCeylonModulesOutputFolder(project).getRawLocation().toFile(); } public static IFolder getCeylonModulesOutputFolder(IProject project) { CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); String outputRepoRelativePath; if (ceylonProject != null) { CeylonProjectConfig config = ceylonProject.getConfiguration(); outputRepoRelativePath = config.getOutputRepoProjectRelativePath(); } else { outputRepoRelativePath = "modules"; } return project.getFolder(outputRepoRelativePath); } public static String getCeylonSystemRepo(IProject project) { String defaultRepo = "${ceylon.repo}"; if (!project.isAccessible()) { return defaultRepo; } String systemRepo = (String) getBuilderArgs(project).get("systemRepo"); if (systemRepo == null || systemRepo.isEmpty()) { return defaultRepo; } return systemRepo; } public static String getInterpolatedCeylonSystemRepo(IProject project) { return interpolateVariablesInRepositoryPath(getCeylonSystemRepo(project)); } public static String[] getDefaultUserRepositories() { return new String[]{ "${ceylon.repo}", "${user.home}/.ceylon/repo", Constants.REPO_URL_CEYLON }; } public static String interpolateVariablesInRepositoryPath(String repoPath) { String userHomePath = System.getProperty("user.home"); String pluginRepoPath = CeylonPlugin.getInstance() .getCeylonRepository() .getAbsolutePath(); return repoPath.replace("${user.home}", userHomePath). replace("${ceylon.repo}", pluginRepoPath); } /** * String representation for debugging purposes */ public String toString() { return this.getProject() == null ? "CeylonBuilder for unknown project" : "CeylonBuilder for " + getProject().getName(); } public static void setContainerInitialized(IProject project) { containersInitialized.add(project); } public static boolean isContainerInitialized(IProject project) { return containersInitialized.contains(project); } public static boolean allClasspathContainersInitialized() { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); for (IProject project : root.getProjects()) { if (project.isAccessible() && CeylonNature.isEnabled(project) && ! containersInitialized.contains(project)) { return false; } } return true; } public static ModuleDependencies getModuleDependenciesForProject( IProject project) { CeylonProject<IProject,IResource,IFolder,IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); if (ceylonProject != null) { return ceylonProject.getModuleDependencies(); } return null; } }