Package dtool.engine

Source Code of dtool.engine.SemanticManager$SM_SearchCompilersOnPath

/*******************************************************************************
* Copyright (c) 2014, 2014 Bruno Medeiros and other Contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Bruno Medeiros - initial API and implementation
*******************************************************************************/
package dtool.engine;

import static melnorme.utilbox.core.CoreUtil.areEqual;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ExecutionException;

import melnorme.utilbox.concurrency.ITaskAgent;
import dtool.dub.BundlePath;
import dtool.dub.DubBundleDescription;
import dtool.dub.DubBundleDescription.DubDescribeAnalysis;
import dtool.dub.DubHelper.RunDubDescribeCallable;
import dtool.dub.ResolvedManifest;
import dtool.engine.AbstractBundleResolution.ResolvedModule;
import dtool.engine.ModuleParseCache.ParseSourceException;
import dtool.engine.StandardLibraryResolution.MissingStandardLibraryResolution;
import dtool.engine.compiler_installs.CompilerInstall;
import dtool.engine.compiler_installs.CompilerInstallDetector;
import dtool.engine.compiler_installs.SearchCompilersOnPathOperation;
import dtool.engine.modules.BundleModulesVisitor;
import dtool.engine.modules.ModuleFullName;
import dtool.engine.util.CachingRegistry;
import dtool.engine.util.FileCachingEntry;
import dtool.parser.DeeParserResult.ParsedModule;

/**
* A caching hierarchical registry.
* This class could be generalized, but it would be a bit of a mess to handle all the parameterized types.
*/
abstract class AbstractSemanticManager {
 
  public AbstractSemanticManager() {
  }
 
  protected final CachingRegistry<BundlePath, BundleInfo> infos = new CachingRegistry<BundlePath, BundleInfo>() {
    @Override
    protected BundleInfo createEntry(BundlePath bundlePath) {
      return new BundleInfo(bundlePath);
    }
  };
 
  protected final Object entriesLock = new Object();
 
  /* -----------------  ----------------- */
 
  protected class BundleInfo {
   
    protected final BundlePath bundlePath;
   
    protected final FileCachingEntry<ResolvedManifest> manifestEntry;
    protected volatile BundleResolution bundleResolution;
   
    public BundleInfo(BundlePath bundlePath) {
      this.bundlePath = bundlePath;
      this.manifestEntry = new FileCachingEntry<>(bundlePath.path);
    }
   
    public ResolvedManifest getManifest() {
      return manifestEntry.getValue();
    }
   
    public BundleResolution getSemanticResolution() {
      return bundleResolution;
    }
   
    public boolean checkIsManifestStale() {
      if(manifestEntry.isStale()) {
        return true;
      }
     
      synchronized(entriesLock) {
       
        for(ResolvedManifest depBundle : getManifest().getBundleDeps()) {
          BundleInfo depBundleInfo = getInfo(depBundle.getBundlePath());
          if(depBundle != depBundleInfo.getManifest()) {
            // Dep manifest is not stale, but was updated in the meanwhile.
            // Therefore parent is stale.
            return true;
          }
          if(depBundleInfo.checkIsManifestStale()) {
            return true;
          }
        }
        return false;
      }
    }
   
    public boolean checkIsResolutionStale() {
      return
          getSemanticResolution() == null ||
          checkIsManifestStale() ||         
          getSemanticResolution().checkIsStale();
    }
   
  }
 
  protected BundleInfo getInfo(BundlePath bundlePath) {
    return infos.getEntry(bundlePath);
  }
 
  public ResolvedManifest getStoredManifest(BundlePath bundlePath) {
    BundleInfo info = infos.getEntryOrNull(bundlePath);
    return info != null ? info.getManifest() : null;
  }
 
  public boolean checkIsManifestStale(BundlePath bundlePath) {
    BundleInfo info = infos.getEntryOrNull(bundlePath);
    return info == null ? true : info.checkIsManifestStale();
  }
 
  public ResolvedManifest getUpdatedManifest(BundlePath bundlePath) throws ExecutionException {
    BundleInfo info = getInfo(bundlePath);
    if(info.checkIsManifestStale()) {
      return updateManifestEntry(bundlePath);
    }
    return info.getManifest();
  }
 
  protected final Object updateOperationLock = new Object();
 
  protected abstract ResolvedManifest updateManifestEntry(BundlePath bundlePath) throws ExecutionException;
 
}

/**
* Maintains a registry of parsed bundle manifests, indexed by bundle path.
*/
public class SemanticManager extends AbstractSemanticManager {
 
  protected final DToolServer dtoolServer;
  protected final ITaskAgent dubProcessAgent;
  protected final ModuleParseCache parseCache;
 
  public SemanticManager(DToolServer dtoolServer) {
    this.dtoolServer = dtoolServer;
    this.parseCache = new ModuleParseCache(dtoolServer);
    this.dubProcessAgent = dtoolServer.new DToolTaskAgent("DToolServer.DubProcessAgent");
  }
 
  public void shutdown() {
    dubProcessAgent.shutdownNow();
  }
 
  public ModuleParseCache getParseCache() {
    return parseCache;
  }
 
  @Override
  protected ResolvedManifest updateManifestEntry(BundlePath bundlePath) throws ExecutionException {
    synchronized(updateOperationLock) {
      // Recheck stale status after acquiring lock, it might have been updated in the meanwhile.
      // Otherwise unnecessary updates might occur after one other.
      BundleInfo info = getInfo(bundlePath);
      if(info.checkIsManifestStale() == false)
        return info.getManifest();
     
      doUpdateManifestEntry(bundlePath);
      return info.getManifest();
    }
  }
 
  protected void doUpdateManifestEntry(BundlePath bundlePath) throws ExecutionException {
    RunDubDescribeCallable dubDescribeTask = new RunDubDescribeCallable(bundlePath, false);
    DubBundleDescription bundleDesc = dubDescribeTask.submitAndGet(dubProcessAgent);
   
    FileTime dubStartTimeStamp = dubDescribeTask.getStartTimeStamp();
    DubDescribeAnalysis dubDescribeAnalyzer = new DubDescribeAnalysis(bundleDesc);
   
    setNewManifestEntry(dubStartTimeStamp, dubDescribeAnalyzer);
  }
 
  protected void setNewManifestEntry(FileTime dubStartTimeStamp, DubDescribeAnalysis dubDescribeAnalyzer) {
    synchronized(entriesLock) {
      for(ResolvedManifest newManifestValue : dubDescribeAnalyzer.getAllManifests()) {
        dtoolServer.logMessage("Resolved new manifest for: " + newManifestValue.bundlePath +
          "\n  timestamp: " + dubStartTimeStamp.toString());
       
        // We cap the maximum timestamp because DUB describe is only guaranteed to have read the
        // manifest files up to dubStartTimeStamp
        getInfo(newManifestValue.bundlePath).manifestEntry.updateValue(newManifestValue, dubStartTimeStamp);
      }
    }
  }
 
  /* ----------------- Semantic Resolution and module list ----------------- */
 
  public BundleResolution getStoredResolution(BundlePath bundlePath) {
    BundleInfo info = infos.getEntryOrNull(bundlePath);
    return info != null ? info.getSemanticResolution() : null;
  }
 
  public boolean checkIsResolutionStale(BundlePath bundlePath) {
    BundleInfo info = infos.getEntryOrNull(bundlePath);
    return info == null ? true : info.checkIsResolutionStale();
  }
 
  protected class SM_BundleModulesVisitor extends BundleModulesVisitor {
    public SM_BundleModulesVisitor(List<Path> importFolders) {
      super(importFolders);
    }
   
    @Override
    protected FileVisitResult handleFileVisitException(Path file, IOException exc) {
      dtoolServer.logError("Error visiting directory/file path: " + file, exc);
      return FileVisitResult.CONTINUE;
    }
  }
 
  public BundleResolution getUpdatedResolution(BundlePath bundlePath) throws ExecutionException {
    return getUpdatedResolution(bundlePath, null);
  }
 
  public BundleResolution getUpdatedResolution(BundlePath bundlePath, Path compilerPath) throws ExecutionException {
    BundleInfo info = getInfo(bundlePath);
    BundleResolution semanticResolution = info.getSemanticResolution();
    if(info.checkIsResolutionStale() ||
      (compilerPath != null && !areEqual(semanticResolution.getCompilerPath(), compilerPath))) {
      /*FIXME: BUG here*/
      return updateSemanticResolutionEntry(info, compilerPath);
    }
    return semanticResolution;
  }
 
  protected BundleResolution updateSemanticResolutionEntry(BundleInfo staleInfo, Path compilerPath)
      throws ExecutionException {
    synchronized(updateOperationLock) {
      // Recheck stale status after acquiring lock, it might have been updated in the meanwhile.
      // Otherwise unnecessary update operatons might occur if two threads tried to update at the same time.
      if(staleInfo.checkIsResolutionStale() == false)
        return staleInfo.getSemanticResolution();
     
      BundlePath bundlePath = staleInfo.bundlePath;
      ResolvedManifest manifest = getUpdatedManifest(bundlePath);
      StandardLibraryResolution stdLibResolution = getUpdatedStdLibResolution(compilerPath);
     
      BundleResolution bundleRes = new BundleResolution(this, manifest, stdLibResolution);
     
      setNewBundleResolutionEntry(bundleRes);
      return staleInfo.getSemanticResolution();
    }
  }
 
  protected BundleInfo setNewBundleResolutionEntry(BundleResolution bundleRes) {
    synchronized(entriesLock) {
      for(BundleResolution newDepBundleRes : bundleRes.getDirectDependencies()) {
        setNewBundleResolutionEntry(newDepBundleRes);
      }
      BundleInfo newInfo = getInfo(bundleRes.getBundlePath());
      newInfo.bundleResolution = bundleRes;
      return newInfo;
    }
  }
 
  /* ----------------- StdLib resolution ----------------- */
 
  protected class SM_SearchCompilersOnPath extends SearchCompilersOnPathOperation {
    @Override
    protected void handleWarning(String message) {
      dtoolServer.logMessage(message);
    }
  }
 
  protected StandardLibraryResolution getUpdatedStdLibResolution(Path compilerPath) {
    CompilerInstall foundInstall = getCompilerInstallForNewResolution(compilerPath);
    return stdLibResolutions.getEntry(foundInstall); // found install can be null
  }
 
  protected CompilerInstall getCompilerInstallForNewResolution(Path compilerPath) {
    if(compilerPath != null) {
      return new CompilerInstallDetector().detectInstallFromCompilerCommandPath(compilerPath);
    } else {
      // TODO: determine a better match according to compiler type.
      return new SM_SearchCompilersOnPath().searchForCompilersInDefaultPathEnvVars().getPreferredInstall();
    }
  }
 
  protected final StdLibResolutionsCache stdLibResolutions = new StdLibResolutionsCache();
 
  protected class StdLibResolutionsCache extends CachingRegistry<CompilerInstall, StandardLibraryResolution> {
    @Override
    public synchronized StandardLibraryResolution getEntry(CompilerInstall key) {
      if(key == null) {
        key = MissingStandardLibraryResolution.NULL_COMPILER_INSTALL;
      }
      StandardLibraryResolution entry = map.get(key);
      if(entry == null || entry.checkIsStale()) {
        entry = createEntry(key);
        map.put(key, entry);
      }
      return entry;
    }
   
    @Override
    protected StandardLibraryResolution createEntry(CompilerInstall compilerInstall) {
      if(compilerInstall == MissingStandardLibraryResolution.NULL_COMPILER_INSTALL) {
        return new MissingStandardLibraryResolution(SemanticManager.this);
      }
      return new StandardLibraryResolution(SemanticManager.this, compilerInstall);
    }
  }
 
  /* ----------------- Working Copy and module resolution ----------------- */
 
  public ParsedModule setWorkingCopyAndParse(Path filePath, String source) {
    return parseCache.setWorkingCopyAndGetParsedModule(filePath, source);
  }
 
  public void discardWorkingCopy(Path filePath) {
    parseCache.discardWorkingCopy(filePath);
  }
 
  public ResolvedModule getUpdatedResolvedModule(Path filePath) throws ExecutionException {
    return getUpdatedResolvedModule(filePath, null);
  }
 
  public ResolvedModule getUpdatedResolvedModule(Path filePath, Path compilerPath) throws ExecutionException {
    // Keep this enabled for now.
//    if(!filePath.isAbsolute()) {
//      throw new ExecutionException(new Exception("Invalid module path"));
//    }
    BundlePath bundlePath = BundlePath.findBundleForPath(filePath);
   
    try {
      AbstractBundleResolution bundleRes;
      if(bundlePath == null) {
        StandardLibraryResolution stdLibResolution = getUpdatedStdLibResolution(compilerPath);
        bundleRes = new SyntheticBundleResolution(this, BundleModules.createEmpty(), stdLibResolution);
      } else {
        bundleRes = getUpdatedResolution(bundlePath, compilerPath);
      }
      return bundleRes.getBundleResolvedModule(filePath);
    } catch (ParseSourceException e) {
      throw new ExecutionException(e);
    }
  }
 
  protected class SyntheticBundleResolution extends AbstractBundleResolution {
   
    protected final StandardLibraryResolution stdLibResolution;

    public SyntheticBundleResolution(SemanticManager manager, BundleModules bundleModules,
        StandardLibraryResolution stdLibResolution) {
      super(manager, bundleModules);
      this.stdLibResolution = stdLibResolution;
    }
   
    @Override
    protected void findModules(String fullNamePrefix, HashSet<String> matchedModules) {
      stdLibResolution.findModules(fullNamePrefix, matchedModules);
    }
   
    @Override
    public ResolvedModule findResolvedModule(ModuleFullName moduleFullName) throws ParseSourceException {
      return stdLibResolution.findResolvedModule(moduleFullName);
    }
   
  }
 
}
TOP

Related Classes of dtool.engine.SemanticManager$SM_SearchCompilersOnPath

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.