Package org.intellij.erlang.compilation

Source Code of org.intellij.erlang.compilation.ErlangPrepareDependenciesCompileTask$ErlangModulesDependencyGraph$Node

/*
* Copyright 2012-2014 Sergey Ignatov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.intellij.erlang.compilation;

import com.intellij.compiler.server.BuildManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompileTask;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Function;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
import com.intellij.util.xmlb.XmlSerializationException;
import com.intellij.util.xmlb.XmlSerializer;
import org.intellij.erlang.utils.ErlangModulesUtil;
import org.intellij.erlang.facet.ErlangFacet;
import org.intellij.erlang.jps.builder.ErlangBuilder;
import org.intellij.erlang.jps.builder.ErlangModuleBuildOrderDescriptor;
import org.intellij.erlang.jps.builder.ErlangModuleBuildOrders;
import org.intellij.erlang.psi.ErlangFile;
import org.intellij.erlang.psi.impl.ErlangPsiImplUtil;
import org.jdom.Document;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;

import java.io.File;
import java.io.IOException;
import java.util.*;


public class ErlangPrepareDependenciesCompileTask implements CompileTask {
  @Override
  public boolean execute(final CompileContext context) {
    File projectSystemDirectory = BuildManager.getInstance().getProjectSystemDirectory(context.getProject());
    if (projectSystemDirectory == null) {
      addPrepareDependenciesFailedMessage(context);
      return true;
    }

    ErlangModuleBuildOrders buildOrders = ApplicationManager.getApplication().runReadAction(new Computable<ErlangModuleBuildOrders>() {
      @Nullable
      @Override
      public ErlangModuleBuildOrders compute() {
        return getModuleBuildOrders(context);
      }
    });
    if (buildOrders == null) {
      return false; // errors are reported to context.
    }
    try {
      Document serializedBuildOrders = new Document(XmlSerializer.serialize(buildOrders, new SkipDefaultValuesSerializationFilters()));
      File file = new File(projectSystemDirectory, ErlangBuilder.DEPENDENCIES_CONFIG_FILE_PATH);
      //noinspection ResultOfMethodCallIgnored
      file.getParentFile().mkdirs();
      JDOMUtil.writeDocument(serializedBuildOrders, file, SystemProperties.getLineSeparator());
    } catch (XmlSerializationException e) {
      addPrepareDependenciesFailedMessage(context);
      return true;
    } catch (IOException e) {
      addPrepareDependenciesFailedMessage(context);
      return true;
    }

    return true;
  }

  public static void addPrepareDependenciesFailedMessage(CompileContext context) {
    context.addMessage(CompilerMessageCategory.WARNING, "Failed to submit dependencies info to compiler. Parse transform failures may occur.", null, -1, -1);
  }

  @Nullable
  private static ErlangModuleBuildOrders getModuleBuildOrders(CompileContext context) {
    Module[] modulesToCompile = context.getCompileScope().getAffectedModules();
    ErlangModuleBuildOrders buildOrders = new ErlangModuleBuildOrders(modulesToCompile.length);
    try {
      for (Module module : modulesToCompile) {
        buildOrders.myModuleBuildOrderDescriptors.add(getModuleBuildOrderInner(module));
      }
    } catch (CyclicDependencyFoundException e) {
      context.addMessage(CompilerMessageCategory.ERROR, "Cyclic erlang module dependency detected. Check parse_transform usages.", null, -1, -1);
      return null;
    }
    return buildOrders;
  }

  // It's public for test purposes only
  @TestOnly
  public static ErlangModuleBuildOrderDescriptor getModuleBuildOrder(Module module) throws CyclicDependencyFoundException {
    return getModuleBuildOrderInner(module);
  }

  private static ErlangModuleBuildOrderDescriptor getModuleBuildOrderInner(Module module) throws CyclicDependencyFoundException {
    ErlangModuleBuildOrderDescriptor buildOrder = new ErlangModuleBuildOrderDescriptor();
    ErlangFacet erlangFacet = ErlangFacet.getFacet(module);
    List<String> globalParseTransforms = erlangFacet != null ? erlangFacet.getConfiguration().getParseTransforms() : ContainerUtil.<String>emptyList();
    buildOrder.myModuleName = module.getName();
    buildOrder.myOrderedErlangModulePaths = getTopologicallySortedErlangModulePaths(ErlangModulesUtil.getErlangModules(module, false), globalParseTransforms);
    buildOrder.myOrderedErlangTestModulePaths = getTopologicallySortedErlangModulePaths(ErlangModulesUtil.getErlangModules(module, true), ContainerUtil.<String>emptyList());
    return buildOrder;
  }

  private static List<String> getTopologicallySortedErlangModulePaths(Collection<ErlangFile> erlangModules, List<String> globalParseTransforms) throws CyclicDependencyFoundException {
    ErlangModulesDependencyGraph depsGraph = new ErlangModulesDependencyGraph(erlangModules, globalParseTransforms);
    return ContainerUtil.mapNotNull(depsGraph.getSortedModules(), new Function<ErlangFile, String>() {
      @Nullable
      @Override
      public String fun(ErlangFile erlangFile) {
        VirtualFile virtualFile = erlangFile.getVirtualFile();
        return virtualFile != null ? virtualFile.getPath() : null;
      }
    });
  }

  private static class ErlangModulesDependencyGraph {
    private final Map<String, Node> myNamesToNodesMap;

    public ErlangModulesDependencyGraph(Collection<ErlangFile> modules, List<String> globalParseTransforms) {
      myNamesToNodesMap = new HashMap<String, Node>(modules.size());
      for (ErlangFile moduleFile : modules) {
        Node node = new Node(moduleFile);
        myNamesToNodesMap.put(node.getModuleName(), node);
      }
      buildDependencies(globalParseTransforms);
    }

    public List<ErlangFile> getSortedModules() throws CyclicDependencyFoundException {
      List<ErlangFile> result = new ArrayList<ErlangFile>(myNamesToNodesMap.size());
      for (Node node : myNamesToNodesMap.values()) {
        if (Node.Status.NONE == node.getStatus()) {
          dfs(node, result);
        }
      }
      return result;
    }

    private static void dfs(Node node, List<ErlangFile> visitedModules) throws CyclicDependencyFoundException {
      node.setStatus(Node.Status.STARTED);
      for (Node dep : node.getDependencies()) {
        switch (dep.getStatus()) {
          case NONE: {
            dfs(dep, visitedModules);
          }
          case COMPLETED: {
            break;
          }
          case STARTED: {
            throw new CyclicDependencyFoundException();
          }
        }
      }
      node.setStatus(Node.Status.COMPLETED);
      visitedModules.add(node.getModuleFile());
    }

    private void buildDependencies(List<String> globalParseTransforms) {
      List<Node> globalPtNodes = getModuleNodes(globalParseTransforms);
      for (Node module : myNamesToNodesMap.values()) {
        Set<String> moduleNames = new HashSet<String>();
        moduleNames.addAll(ErlangPsiImplUtil.getAppliedParseTransformModuleNames(module.getModuleFile()));
        moduleNames.addAll(ErlangPsiImplUtil.getImplementedBehaviourModuleNames(module.getModuleFile()));
        List<Node> dependencies = getModuleNodes(moduleNames);
        module.addDependencies(dependencies);
        module.addDependencies(globalPtNodes);
      }
    }

    private List<Node> getModuleNodes(Collection<String> parseTransforms) {
      ArrayList<Node> ptNodes = new ArrayList<Node>(parseTransforms.size());
      for (String pt : parseTransforms) {
        ContainerUtil.addIfNotNull(myNamesToNodesMap.get(pt), ptNodes);
      }
      return ptNodes;
    }

    private static class Node {
      enum Status {STARTED, COMPLETED, NONE}

      private Status myStatus = Status.NONE;
      private final ErlangFile myModuleFile;
      private final ArrayList<Node> myDependencies = new ArrayList<Node>();

      Node(ErlangFile moduleFile) {
        myModuleFile = moduleFile;
      }

      private Status getStatus() {
        return myStatus;
      }

      private void setStatus(Status status) {
        myStatus = status;
      }

      ErlangFile getModuleFile() {
        return myModuleFile;
      }

      String getModuleName() {
        return FileUtil.getNameWithoutExtension(myModuleFile.getName());
      }

      void addDependency(@Nullable Node dep) {
        if (dep != null && dep != this) {
          myDependencies.add(dep);
        }
      }

      void addDependencies(Collection<Node> deps) {
        for (Node dep : deps) {
          addDependency(dep);
        }
      }

      List<Node> getDependencies() {
        return myDependencies;
      }
    }
  }

  public static class CyclicDependencyFoundException extends Exception {
    CyclicDependencyFoundException() {
    }
  }
}
TOP

Related Classes of org.intellij.erlang.compilation.ErlangPrepareDependenciesCompileTask$ErlangModulesDependencyGraph$Node

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.