Example #1
0
 public static final <T extends TransitiveInfoProvider> Iterable<T> getTransitivePrerequisites(
     RuleContext ruleContext, Mode mode, final Class<T> classType) {
   IterablesChain.Builder<T> builder = IterablesChain.builder();
   for (String attr : TRANSITIVE_ATTRIBUTES) {
     if (ruleContext.getAttribute(attr) != null) {
       builder.add(ruleContext.getPrerequisites(attr, mode, classType));
     }
   }
   return builder.build();
 }
Example #2
0
 private static IterablesChain<Artifact> getArchiveInputs(
     JavaTargetAttributes attributes, Function<Artifact, Artifact> derivedJarFunction) {
   IterablesChain.Builder<Artifact> inputs = IterablesChain.builder();
   inputs.add(
       ImmutableList.copyOf(
           Iterables.transform(attributes.getRuntimeClassPathForArchive(), derivedJarFunction)));
   // TODO(bazel-team): Remove?  Resources not used as input to singlejar action
   inputs.add(ImmutableList.copyOf(attributes.getResources().values()));
   inputs.add(attributes.getClassPathResources());
   return inputs.build();
 }
Example #3
0
/** Utility for configuring an action to generate a deploy archive. */
public class DeployArchiveBuilder {
  /**
   * Memory consumption of SingleJar is about 250 bytes per entry in the output file. Unfortunately,
   * the JVM tends to kill the process with an OOM long before we're at the limit. In the most
   * recent example, 400 MB of memory was enough for about 500,000 entries.
   */
  private static final String SINGLEJAR_MAX_MEMORY = "-Xmx1600m";

  private final RuleContext ruleContext;

  private final IterablesChain.Builder<Artifact> runtimeJarsBuilder = IterablesChain.builder();

  private final JavaSemantics semantics;

  private JavaTargetAttributes attributes;
  private boolean includeBuildData;
  private Compression compression = Compression.UNCOMPRESSED;
  @Nullable private Artifact runfilesMiddleman;
  private Artifact outputJar;
  @Nullable private String javaStartClass;
  private ImmutableList<String> deployManifestLines = ImmutableList.of();
  @Nullable private Artifact launcher;
  private Function<Artifact, Artifact> derivedJars = Functions.identity();

  /** Type of compression to apply to output archive. */
  public enum Compression {

    /** Output should be compressed */
    COMPRESSED,

    /** Output should not be compressed */
    UNCOMPRESSED;
  }

  /** Creates a builder using the configuration of the rule as the action configuration. */
  public DeployArchiveBuilder(JavaSemantics semantics, RuleContext ruleContext) {
    this.ruleContext = ruleContext;
    this.semantics = semantics;
  }

  /** Sets the processed attributes of the rule generating the deploy archive. */
  public DeployArchiveBuilder setAttributes(JavaTargetAttributes attributes) {
    this.attributes = attributes;
    return this;
  }

  /** Sets whether to include build-data.properties in the deploy archive. */
  public DeployArchiveBuilder setIncludeBuildData(boolean includeBuildData) {
    this.includeBuildData = includeBuildData;
    return this;
  }

  /** Sets whether to enable compression of the output deploy archive. */
  public DeployArchiveBuilder setCompression(Compression compress) {
    this.compression = Preconditions.checkNotNull(compress);
    return this;
  }

  /**
   * Sets additional dependencies to be added to the action that creates the deploy jar so that we
   * force the runtime dependencies to be built.
   */
  public DeployArchiveBuilder setRunfilesMiddleman(@Nullable Artifact runfilesMiddleman) {
    this.runfilesMiddleman = runfilesMiddleman;
    return this;
  }

  /** Sets the artifact to create with the action. */
  public DeployArchiveBuilder setOutputJar(Artifact outputJar) {
    this.outputJar = Preconditions.checkNotNull(outputJar);
    return this;
  }

  /** Sets the class to launch the Java application. */
  public DeployArchiveBuilder setJavaStartClass(@Nullable String javaStartClass) {
    this.javaStartClass = javaStartClass;
    return this;
  }

  /** Adds additional jars that should be on the classpath at runtime. */
  public DeployArchiveBuilder addRuntimeJars(Iterable<Artifact> jars) {
    this.runtimeJarsBuilder.add(jars);
    return this;
  }

  /** Sets the list of extra lines to add to the archive's MANIFEST.MF file. */
  public DeployArchiveBuilder setDeployManifestLines(Iterable<String> deployManifestLines) {
    this.deployManifestLines = ImmutableList.copyOf(deployManifestLines);
    return this;
  }

  /** Sets the optional launcher to be used as the executable for this deploy JAR */
  public DeployArchiveBuilder setLauncher(@Nullable Artifact launcher) {
    this.launcher = launcher;
    return this;
  }

  public DeployArchiveBuilder setDerivedJarFunction(Function<Artifact, Artifact> derivedJars) {
    this.derivedJars = derivedJars;
    return this;
  }

  public static CustomCommandLine.Builder defaultSingleJarCommandLine(
      Artifact outputJar,
      String javaMainClass,
      ImmutableList<String> deployManifestLines,
      Iterable<Artifact> buildInfoFiles,
      ImmutableList<Artifact> classpathResources,
      Iterable<Artifact> runtimeClasspath,
      boolean includeBuildData,
      Compression compress,
      Artifact launcher) {

    CustomCommandLine.Builder args = CustomCommandLine.builder();
    args.addExecPath("--output", outputJar);
    if (compress == Compression.COMPRESSED) {
      args.add("--compression");
    }
    args.add("--normalize");
    if (javaMainClass != null) {
      args.add("--main_class");
      args.add(javaMainClass);
    }

    if (!deployManifestLines.isEmpty()) {
      args.add("--deploy_manifest_lines");
      args.add(deployManifestLines);
    }

    if (buildInfoFiles != null) {
      for (Artifact artifact : buildInfoFiles) {
        args.addExecPath("--build_info_file", artifact);
      }
    }
    if (!includeBuildData) {
      args.add("--exclude_build_data");
    }
    if (launcher != null) {
      args.add("--java_launcher");
      args.add(launcher.getExecPathString());
    }

    args.addExecPaths("--classpath_resources", classpathResources);
    args.addExecPaths("--sources", runtimeClasspath);
    return args;
  }

  /** Computes input artifacts for a deploy archive based on the given attributes. */
  public static IterablesChain<Artifact> getArchiveInputs(JavaTargetAttributes attributes) {
    return getArchiveInputs(attributes, Functions.<Artifact>identity());
  }

  private static IterablesChain<Artifact> getArchiveInputs(
      JavaTargetAttributes attributes, Function<Artifact, Artifact> derivedJarFunction) {
    IterablesChain.Builder<Artifact> inputs = IterablesChain.builder();
    inputs.add(
        ImmutableList.copyOf(
            Iterables.transform(attributes.getRuntimeClassPathForArchive(), derivedJarFunction)));
    // TODO(bazel-team): Remove?  Resources not used as input to singlejar action
    inputs.add(ImmutableList.copyOf(attributes.getResources().values()));
    inputs.add(attributes.getClassPathResources());
    return inputs.build();
  }

  /** Builds the action as configured. */
  public void build() throws InterruptedException {
    ImmutableList<Artifact> classpathResources = attributes.getClassPathResources();
    Set<String> classPathResourceNames = new HashSet<>();
    for (Artifact artifact : classpathResources) {
      String name = artifact.getExecPath().getBaseName();
      if (!classPathResourceNames.add(name)) {
        ruleContext.attributeError(
            "classpath_resources",
            "entries must have different file names (duplicate: " + name + ")");
        return;
      }
    }

    IterablesChain<Artifact> runtimeJars = runtimeJarsBuilder.build();

    // TODO(kmb): Consider not using getArchiveInputs, specifically because we don't want/need to
    // transform anything but the runtimeClasspath and b/c we currently do it twice here and below
    IterablesChain.Builder<Artifact> inputs = IterablesChain.builder();
    inputs.add(getArchiveInputs(attributes, derivedJars));

    inputs.add(ImmutableList.copyOf(Iterables.transform(runtimeJars, derivedJars)));
    if (runfilesMiddleman != null) {
      inputs.addElement(runfilesMiddleman);
    }

    ImmutableList<Artifact> buildInfoArtifacts = ruleContext.getBuildInfo(JavaBuildInfoFactory.KEY);
    inputs.add(buildInfoArtifacts);

    Iterable<Artifact> runtimeClasspath =
        Iterables.transform(
            Iterables.concat(runtimeJars, attributes.getRuntimeClassPathForArchive()), derivedJars);

    if (launcher != null) {
      inputs.addElement(launcher);
    }

    CommandLine commandLine =
        semantics.buildSingleJarCommandLine(
            ruleContext.getConfiguration(),
            outputJar,
            javaStartClass,
            deployManifestLines,
            buildInfoArtifacts,
            classpathResources,
            runtimeClasspath,
            includeBuildData,
            compression,
            launcher);

    List<String> jvmArgs = ImmutableList.of("-client", SINGLEJAR_MAX_MEMORY);
    ResourceSet resourceSet =
        ResourceSet.createWithRamCpuIo(/*memoryMb = */ 200.0, /*cpuUsage = */ .2, /*ioUsage=*/ .2);

    // If singlejar's name ends with .jar, it is Java application, otherwise it is native.
    // TODO(asmundak): once b/28640279 is fixed (that is, the native singlejar is released),
    // eliminate this check, allowing only native singlejar.
    Artifact singlejar = getSingleJar(ruleContext);
    if (singlejar.getFilename().endsWith(".jar")) {
      ruleContext.registerAction(
          new SpawnAction.Builder()
              .addInputs(inputs.build())
              .addTransitiveInputs(JavaHelper.getHostJavabaseInputs(ruleContext))
              .addOutput(outputJar)
              .setResources(resourceSet)
              .setJarExecutable(
                  ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable(),
                  singlejar,
                  jvmArgs)
              .setCommandLine(commandLine)
              .alwaysUseParameterFile(ParameterFileType.SHELL_QUOTED)
              .setProgressMessage("Building deploy jar " + outputJar.prettyPrint())
              .setMnemonic("JavaDeployJar")
              .setExecutionInfo(ImmutableMap.of("supports-workers", "1"))
              .build(ruleContext));
    } else {
      ruleContext.registerAction(
          new SpawnAction.Builder()
              .addInputs(inputs.build())
              .addTransitiveInputs(JavaHelper.getHostJavabaseInputs(ruleContext))
              .addOutput(outputJar)
              .setResources(resourceSet)
              .setExecutable(singlejar)
              .setCommandLine(commandLine)
              .alwaysUseParameterFile(ParameterFileType.SHELL_QUOTED)
              .setProgressMessage("Building deploy jar " + outputJar.prettyPrint())
              .setMnemonic("JavaDeployJar")
              .build(ruleContext));
    }
  }

  /** Returns the SingleJar deploy jar Artifact. */
  private static Artifact getSingleJar(RuleContext ruleContext) {
    Artifact singleJar = JavaToolchainProvider.fromRuleContext(ruleContext).getSingleJar();
    if (singleJar != null) {
      return singleJar;
    }
    return ruleContext.getPrerequisiteArtifact("$singlejar", Mode.HOST);
  }
}
Example #4
0
  /** Builds the action as configured. */
  public void build() throws InterruptedException {
    ImmutableList<Artifact> classpathResources = attributes.getClassPathResources();
    Set<String> classPathResourceNames = new HashSet<>();
    for (Artifact artifact : classpathResources) {
      String name = artifact.getExecPath().getBaseName();
      if (!classPathResourceNames.add(name)) {
        ruleContext.attributeError(
            "classpath_resources",
            "entries must have different file names (duplicate: " + name + ")");
        return;
      }
    }

    IterablesChain<Artifact> runtimeJars = runtimeJarsBuilder.build();

    // TODO(kmb): Consider not using getArchiveInputs, specifically because we don't want/need to
    // transform anything but the runtimeClasspath and b/c we currently do it twice here and below
    IterablesChain.Builder<Artifact> inputs = IterablesChain.builder();
    inputs.add(getArchiveInputs(attributes, derivedJars));

    inputs.add(ImmutableList.copyOf(Iterables.transform(runtimeJars, derivedJars)));
    if (runfilesMiddleman != null) {
      inputs.addElement(runfilesMiddleman);
    }

    ImmutableList<Artifact> buildInfoArtifacts = ruleContext.getBuildInfo(JavaBuildInfoFactory.KEY);
    inputs.add(buildInfoArtifacts);

    Iterable<Artifact> runtimeClasspath =
        Iterables.transform(
            Iterables.concat(runtimeJars, attributes.getRuntimeClassPathForArchive()), derivedJars);

    if (launcher != null) {
      inputs.addElement(launcher);
    }

    CommandLine commandLine =
        semantics.buildSingleJarCommandLine(
            ruleContext.getConfiguration(),
            outputJar,
            javaStartClass,
            deployManifestLines,
            buildInfoArtifacts,
            classpathResources,
            runtimeClasspath,
            includeBuildData,
            compression,
            launcher);

    List<String> jvmArgs = ImmutableList.of("-client", SINGLEJAR_MAX_MEMORY);
    ResourceSet resourceSet =
        ResourceSet.createWithRamCpuIo(/*memoryMb = */ 200.0, /*cpuUsage = */ .2, /*ioUsage=*/ .2);

    // If singlejar's name ends with .jar, it is Java application, otherwise it is native.
    // TODO(asmundak): once b/28640279 is fixed (that is, the native singlejar is released),
    // eliminate this check, allowing only native singlejar.
    Artifact singlejar = getSingleJar(ruleContext);
    if (singlejar.getFilename().endsWith(".jar")) {
      ruleContext.registerAction(
          new SpawnAction.Builder()
              .addInputs(inputs.build())
              .addTransitiveInputs(JavaHelper.getHostJavabaseInputs(ruleContext))
              .addOutput(outputJar)
              .setResources(resourceSet)
              .setJarExecutable(
                  ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable(),
                  singlejar,
                  jvmArgs)
              .setCommandLine(commandLine)
              .alwaysUseParameterFile(ParameterFileType.SHELL_QUOTED)
              .setProgressMessage("Building deploy jar " + outputJar.prettyPrint())
              .setMnemonic("JavaDeployJar")
              .setExecutionInfo(ImmutableMap.of("supports-workers", "1"))
              .build(ruleContext));
    } else {
      ruleContext.registerAction(
          new SpawnAction.Builder()
              .addInputs(inputs.build())
              .addTransitiveInputs(JavaHelper.getHostJavabaseInputs(ruleContext))
              .addOutput(outputJar)
              .setResources(resourceSet)
              .setExecutable(singlejar)
              .setCommandLine(commandLine)
              .alwaysUseParameterFile(ParameterFileType.SHELL_QUOTED)
              .setProgressMessage("Building deploy jar " + outputJar.prettyPrint())
              .setMnemonic("JavaDeployJar")
              .build(ruleContext));
    }
  }