Package org.jetbrains.osgi.jps.build

Source Code of org.jetbrains.osgi.jps.build.OsgiBuildSession

package org.jetbrains.osgi.jps.build;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.module.JpsDependencyElement;
import org.jetbrains.jps.model.module.JpsLibraryDependency;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.osgi.jps.model.JpsOsmorcExtensionService;
import org.jetbrains.osgi.jps.model.JpsOsmorcModuleExtension;
import org.jetbrains.osgi.jps.model.LibraryBundlificationRule;
import org.jetbrains.osgi.jps.model.OsmorcJarContentEntry;
import org.jetbrains.osgi.jps.util.OsgiBuildUtil;
import org.jetbrains.jps.util.JpsPathUtil;
import org.osgi.framework.Constants;

import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class OsgiBuildSession implements Reporter {
  private static final Logger LOG = Logger.getInstance(OsgiBuildSession.class);

  /**
   * Condition which matches order entries that are not representing a framework library.
   */
  public static final Condition<JpsDependencyElement> NOT_FRAMEWORK_LIBRARY_CONDITION = new Condition<JpsDependencyElement>() {
    @Override
    public boolean value(JpsDependencyElement entry) {
      if (entry instanceof JpsLibraryDependency) {
        JpsLibraryDependency libEntry = (JpsLibraryDependency)entry;
        String libraryName = libEntry.getLibraryReference().getLibraryName();
        if (libraryName.startsWith("Osmorc:")) {
          return false;
        }
      }
      return true;
    }
  };

  private CompileContext myContext;
  private JpsOsmorcModuleExtension myExtension;
  private JpsModule myModule;
  private String myMessagePrefix;
  private File myOutputJarFile;
  private File myModuleOutputDir;
  private File myOutputDir;
  private BndWrapper myBndWrapper;
  private String mySourceToReport = null;

  public void build(@NotNull OsmorcBuildTarget target, @NotNull CompileContext context) {
    myContext = context;
    myExtension = target.getExtension();
    myModule = target.getModule();
    myMessagePrefix = "[" + myModule.getName() + "] ";

    progress("Building OSGi bundle");
    try {
      prepare();
      doBuild();
    }
    catch (OsgiBuildException e) {
      error(e.getMessage(), e.getCause(), e.getSourcePath());
    }
  }

  private void prepare() throws OsgiBuildException {
    String jarFileLocation = myExtension.getJarFileLocation();
    if (jarFileLocation.isEmpty()) {
      throw new OsgiBuildException("Bundle path is empty - please check OSGi facet settings.");
    }

    myOutputJarFile = new File(jarFileLocation);
    if (!FileUtil.delete(myOutputJarFile)) {
      throw new OsgiBuildException("Can't delete bundle file '" + myOutputJarFile + "'.");
    }
    if (!FileUtil.createParentDirs(myOutputJarFile)) {
      throw new OsgiBuildException("Cannot create directory for bundle file '" + myOutputJarFile + "'.");
    }

    String moduleOutputUrl = JpsJavaExtensionService.getInstance().getOutputUrl(myModule, false);
    if (moduleOutputUrl == null) {
      throw new OsgiBuildException("Unable to determine the compiler output path for the module.");
    }
    myModuleOutputDir = JpsPathUtil.urlToFile(moduleOutputUrl);

    myOutputDir = BndWrapper.getOutputDir(myModuleOutputDir);

    myBndWrapper = new BndWrapper(this);
  }

  private void doBuild() throws OsgiBuildException {
    progress("Running Bnd to build the bundle");

    File bndFile = getBndFile();

    if (!myExtension.isUseBundlorFile()) {
      mySourceToReport = getSourceFileToReport(bndFile);
      myBndWrapper.build(bndFile, myModuleOutputDir, myOutputJarFile);
      mySourceToReport = null;
    }
    else {
      File tempFile = new File(myOutputJarFile.getAbsolutePath() + ".tmp.jar");
      mySourceToReport = getSourceFileToReport(bndFile);
      myBndWrapper.build(bndFile, myModuleOutputDir, tempFile);
      mySourceToReport = null;

      progress("Running Bundlor to calculate the manifest");

      String bundlorPath = myExtension.getBundlorFileLocation();
      File bundlorFile = OsgiBuildUtil.findFileInModuleContentRoots(myModule, bundlorPath);
      if (bundlorFile == null) {
        throw new OsgiBuildException("Bundlor file missing '" + bundlorPath + "' - please check OSGi facet settings.");
      }

      try {
        Properties properties = OsgiBuildUtil.getMavenProjectProperties(myContext, myModule);
        List<String> warnings = new BundlorWrapper().wrapModule(properties, tempFile, myOutputJarFile, bundlorFile);
        for (String warning : warnings) {
          warning(warning, null, bundlorFile.getPath());
        }
      }
      finally {
        if (!FileUtil.delete(tempFile)) {
          warning("Can't delete temporary file '" + tempFile + "'", null, null);
        }
      }
    }

    if (!myExtension.isUseBndFile() && !myExtension.isUseBundlorFile()) {
      progress("Bundling non-OSGi libraries");
      bundlifyLibraries();
    }
  }

  @NotNull
  private File getBndFile() throws OsgiBuildException {
    if (myExtension.isUseBndFile()) {
      String bndPath = myExtension.getBndFileLocation();
      File bndFile = OsgiBuildUtil.findFileInModuleContentRoots(myModule, bndPath);
      if (bndFile != null && bndFile.canRead()) {
        return bndFile;
      }
      throw new OsgiBuildException("Bnd file missing '" + bndPath + "' - please check OSGi facet settings.");
    }

    // use a linked hash map to keep the order of properties.
    Map<String, String> buildProperties = new LinkedHashMap<String, String>();
    if (myExtension.isManifestManuallyEdited() || myExtension.isOsmorcControlsManifest()) {
      if (myExtension.isOsmorcControlsManifest()) {
        // fully osmorc controlled, no bnd file, read in all  properties
        buildProperties.putAll(myExtension.getAdditionalProperties());
        buildProperties.put(Constants.BUNDLE_SYMBOLICNAME, myExtension.getBundleSymbolicName());
        buildProperties.put(Constants.BUNDLE_VERSION, myExtension.getBundleVersion());
        String activator = myExtension.getBundleActivator();
        if (!StringUtil.isEmptyOrSpaces(activator)) buildProperties.put(Constants.BUNDLE_ACTIVATOR, activator);
      }
      else { // manually edited manifest
        File manifestFile = myExtension.getManifestFile();
        if (manifestFile == null) {
          throw new OsgiBuildException("Manifest file '" + myExtension.getManifestLocation() + "' missing - please check OSGi facet settings.");
        }
        buildProperties.put("-manifest", manifestFile.getAbsolutePath());
      }

      StringBuilder pathBuilder = new StringBuilder();

      // add all the class paths to include resources, so stuff from the project gets copied over.
      // XXX: one could argue if this should be done for a non-osmorc build
      pathBuilder.append(myModuleOutputDir.getPath());

      // now include the paths from the configuration
      for (OsmorcJarContentEntry contentEntry : myExtension.getAdditionalJarContents()) {
        if (pathBuilder.length() > 0) pathBuilder.append(",");
        pathBuilder.append(contentEntry.myDestination).append(" = ").append(contentEntry.mySource);
      }

      // and tell bnd what resources to include
      StringBuilder includedResources = new StringBuilder();
      if (!myExtension.isManifestManuallyEdited()) {
        String resources = myExtension.getAdditionalProperties().get("Include-Resource");
        if (resources != null) includedResources.append(resources).append(',');
        includedResources.append(pathBuilder);
      }
      else {
        includedResources.append(pathBuilder);
      }
      buildProperties.put("Include-Resource", includedResources.toString());

      // add the ignore pattern for the resources
      String pattern = myExtension.getIgnoreFilePattern();
      if (!StringUtil.isEmptyOrSpaces(pattern)) {
        try {
          //noinspection ResultOfMethodCallIgnored
          Pattern.compile(pattern);
        }
        catch (PatternSyntaxException e) {
          throw new OsgiBuildException("The file ignore pattern is invalid - please check OSGi facet settings.");
        }
        buildProperties.put("-donotcopy", pattern);
      }

      if (myExtension.isOsmorcControlsManifest()) {
        // support the {local-packages} instruction
        progress("Calculating local packages");
        LocalPackageCollector.addLocalPackages(myModuleOutputDir, buildProperties);
      }
    }
    else if (!myExtension.isUseBundlorFile()) {
      throw new OsgiBuildException("Bundle creation method not specified - please check OSGi facet settings.");
    }

    String comment = "Generated by IDEA for module '" + myModule.getName() + "' in project '" + myModule.getProject().getName() + "'";
    return myBndWrapper.makeBndFile(buildProperties, comment, myOutputDir);
  }

  private String getSourceFileToReport(File bndFile) {
    if (myExtension.isManifestManuallyEdited()) {
      // link warnings/errors to the user-provided manifest file
      File manifestFile = myExtension.getManifestFile();
      if (manifestFile != null) {
        return manifestFile.getPath();
      }
    }
    else {
      File mavenProjectFile = OsgiBuildUtil.getMavenProjectPath(myContext, myModule);
      if (mavenProjectFile != null) {
        // ok it's imported from Maven, link warnings/errors back to pom.xml
        return mavenProjectFile.getPath();
      }
    }
    return bndFile.getPath();
  }

  /**
   * Bundlifies all libraries that belong to the given module and that are not bundles.
   * The bundles are cached, so if the source library does not change, it will not be bundlified again.
   * Returns a string array containing paths of the bundlified libraries.
   */
  @NotNull
  private List<String> bundlifyLibraries() {
    List<LibraryBundlificationRule> libRules = JpsOsmorcExtensionService.getInstance().getLibraryBundlificationRules();

    Collection<File> dependencies = JpsJavaExtensionService.getInstance().enumerateDependencies(Collections.singletonList(myModule))
      .withoutSdk()
      .withoutModuleSourceEntries()
      .withoutDepModules()
      .productionOnly()
      .runtimeOnly()
      .recursively()
      .exportedOnly()
      .satisfying(NOT_FRAMEWORK_LIBRARY_CONDITION)
      .classes()
      .getRoots();

    List<String> result = ContainerUtil.newArrayList();
    for (File dependency : dependencies) {
      String path = dependency.getPath();
      if (CachingBundleInfoProvider.canBeBundlified(path)) {
        try {
          File bundledDependency = myBndWrapper.wrapLibrary(dependency, myOutputDir, libRules);
          if (bundledDependency != null) {
            result.add(bundledDependency.getPath());
          }
        }
        catch (OsgiBuildException e) {
          warning(e.getMessage(), e.getCause(), e.getSourcePath());
        }
      }
      else if (CachingBundleInfoProvider.isBundle(path)) {
        result.add(path);
      }
    }
    return result;
  }

  @Override
  public void progress(@NotNull String message) {
    myContext.processMessage(new ProgressMessage(myMessagePrefix + message));
  }

  @Override
  public void warning(@NotNull String message, @Nullable Throwable t, @Nullable String sourcePath) {
    LOG.warn(message, t);
    if (sourcePath == null) sourcePath = mySourceToReport;
    myContext.processMessage(new CompilerMessage(OsmorcBuilder.ID, BuildMessage.Kind.WARNING, myMessagePrefix + message, sourcePath));
  }

  @Override
  public void error(@NotNull String message, @Nullable Throwable t, @Nullable String sourcePath) {
    LOG.warn(message, t);
    if (sourcePath == null) sourcePath = mySourceToReport;
    myContext.processMessage(new CompilerMessage(OsmorcBuilder.ID, BuildMessage.Kind.ERROR, myMessagePrefix + message, sourcePath));
  }
}
TOP

Related Classes of org.jetbrains.osgi.jps.build.OsgiBuildSession

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.