Skip to content

Commit

Permalink
always use jar manifest for classpath
Browse files Browse the repository at this point in the history
  • Loading branch information
hcoles committed Aug 7, 2018
1 parent 33bbeae commit fab8ddf
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ private void setClassPath(final OptionSet userArgs, final ReportOptions data) {
ClassPath.getClassPathElementsAsPaths(), this.dependencyFilter));
}
if (userArgs.has(this.classPathFile)) {
data.setShouldUseClassPathJar(true);
try (BufferedReader classPathFileBR = new BufferedReader(new FileReader(userArgs.valueOf(this.classPathFile).getAbsoluteFile()))) {
String element;
while ((element = classPathFileBR.readLine()) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ public class ReportOptions {

private boolean verbose = false;
private boolean failWhenNoMutations = false;
private boolean shouldUseClassPathJar = false;

private final Collection<String> outputs = new LinkedHashSet<>();

Expand Down Expand Up @@ -138,10 +137,6 @@ public boolean isVerbose() {
return this.verbose;
}

public boolean shouldUseClassPathJar() {
return this.shouldUseClassPathJar;
}

/**
* @return the reportDir
*/
Expand Down Expand Up @@ -352,10 +347,6 @@ public void setExcludedTestClasses(
this.excludedTestClasses = excludedClasses;
}

public void setShouldUseClassPathJar(boolean shouldUseClassPathJar) {
this.shouldUseClassPathJar = shouldUseClassPathJar;
}

public void addOutputFormats(final Collection<String> formats) {
this.outputs.addAll(formats);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ public class MutationCoverage {
private final CodeSource code;
private final File baseDir;
private final SettingsFactory settings;
private static boolean shouldUseClassPathJar;

public MutationCoverage(final MutationStrategies strategies,
final File baseDir, final CodeSource code, final ReportOptions data,
Expand All @@ -89,7 +88,6 @@ public MutationCoverage(final MutationStrategies strategies,
this.timings = timings;
this.code = code;
this.baseDir = baseDir;
shouldUseClassPathJar = this.data.shouldUseClassPathJar();
}

public CombinedStatistics runReport() throws IOException {
Expand Down Expand Up @@ -319,9 +317,4 @@ private ClassByteArraySource fallbackToClassLoader(final ClassByteArraySource ba
return clSource.getBytes(clazz);
};
}

public static boolean shouldUseClassPathJar() {
return shouldUseClassPathJar;
}

}
24 changes: 8 additions & 16 deletions pitest-entry/src/main/java/org/pitest/process/WrappingProcess.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.pitest.process;

import static org.pitest.functional.prelude.Prelude.or;
import org.pitest.mutationtest.tooling.MutationCoverage;

import java.io.File;
import java.io.IOException;
Expand All @@ -13,10 +12,9 @@
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.jar.Manifest;

import org.pitest.functional.FCollection;
import org.pitest.util.CommandLineUtils;
import org.pitest.util.ManifestUtils;

public class WrappingProcess {

Expand All @@ -42,7 +40,6 @@ public void start() throws IOException {
this.processArgs.getLaunchClassPath());

configureProcessBuilder(processBuilder, this.processArgs.getWorkingDir(),
this.processArgs.getLaunchClassPath(),
this.processArgs.getEnvironmentVariables());

final Process process = processBuilder.start();
Expand All @@ -51,13 +48,10 @@ public void start() throws IOException {
}

private void configureProcessBuilder(ProcessBuilder processBuilder,
File workingDirectory, String initialClassPath,
Map<String, String> environmentVariables) {
File workingDirectory, Map<String, String> environmentVariables) {
processBuilder.directory(workingDirectory);
final Map<String, String> environment = processBuilder.environment();
if (!MutationCoverage.shouldUseClassPathJar()) {
environment.put("CLASSPATH", initialClassPath);
}

for (final Map.Entry<String, String> entry : environmentVariables.entrySet()) {
environment.put(entry.getKey(), entry.getValue());
}
Expand Down Expand Up @@ -94,13 +88,11 @@ private static List<String> createLaunchArgs(String javaProcess,
final List<String> cmd = new ArrayList<>();
cmd.add(javaProcess);

if (MutationCoverage.shouldUseClassPathJar()) {
try {
cmd.add("-classpath");
cmd.add(CommandLineUtils.createClasspathJarFile(new Manifest(), classPath).getAbsolutePath());
} catch (Exception e) {
System.err.println("Could not create classpath jar");
}
try {
cmd.add("-classpath");
cmd.add(ManifestUtils.createClasspathJarFile(classPath).getAbsolutePath());
} catch (Exception e) {
throw new RuntimeException("Unable to create jar to contain classpath",e);
}

cmd.addAll(args);
Expand Down
20 changes: 18 additions & 2 deletions pitest/src/main/java/org/pitest/classpath/ClassPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -32,6 +33,7 @@

import org.pitest.functional.FCollection;
import org.pitest.util.Log;
import org.pitest.util.ManifestUtils;
import org.pitest.util.PitError;
import org.pitest.util.StreamUtil;

Expand All @@ -53,11 +55,10 @@ public ClassPath(final Collection<File> files) {
this(createRoots(FCollection.filter(files, exists())));
}

public ClassPath(List<ClassPathRoot> roots) {
ClassPath(List<ClassPathRoot> roots) {
this.root = new CompoundClassPathRoot(roots);
}


public Collection<String> classNames() {
return this.root.classNames();
}
Expand Down Expand Up @@ -123,9 +124,24 @@ public static Collection<String> getClassPathElementsAsPaths() {
public static Collection<File> getClassPathElementsAsFiles() {
final Set<File> us = new LinkedHashSet<>();
FCollection.mapTo(getClassPathElementsAsAre(), stringToCanonicalFile(), us);

addEntriesFromClasspathManifest(us);
return us;
}

/**
* Because classpaths can become longer than the OS supports pitest creates temporary jar files and places the classpath
* in the manifest where there is no size limit.
*
* We must therefore parse them out again here.
*
* @param elements existing elements
*/
private static void addEntriesFromClasspathManifest(final Set<File> elements) {
Optional<File> maybeJar = elements.stream().filter( f -> f.getName().startsWith("classpath") && f.getName().endsWith(".jar"))
.findFirst();
maybeJar.ifPresent(file -> elements.addAll(ManifestUtils.readClasspathManifest(file)));
}

public Collection<String> findClasses(final Predicate<String> nameFilter) {
return FCollection.filter(classNames(), nameFilter);
Expand Down
51 changes: 0 additions & 51 deletions pitest/src/main/java/org/pitest/util/CommandLineUtils.java

This file was deleted.

87 changes: 87 additions & 0 deletions pitest/src/main/java/org/pitest/util/ManifestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* 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.pitest.util;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.jar.Attributes;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
* Ugly static methods to create and parse classpath manifests
*/
public class ManifestUtils {

// Method based on
// https://github.com/JetBrains/intellij-community/blob/master/java/java-runtime/src/com/intellij/rt/execution/testFrameworks/ForkedByModuleSplitter.java
// JetBrains copyright notice and licence retained above.
public static File createClasspathJarFile(String classpath)
throws IOException {
final Manifest manifest = new Manifest();
final Attributes attributes = manifest.getMainAttributes();
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");

String classpathForManifest = "";
int idx = 0;
int endIdx = 0;
while (endIdx >= 0) {
endIdx = classpath.indexOf(File.pathSeparator, idx);
String path = endIdx < 0 ? classpath.substring(idx)
: classpath.substring(idx, endIdx);
if (classpathForManifest.length() > 0) {
classpathForManifest += " ";
}

classpathForManifest += new File(path).toURI().toURL().toString();
idx = endIdx + File.pathSeparator.length();
}
attributes.put(Attributes.Name.CLASS_PATH, classpathForManifest);

File jarFile = File.createTempFile("classpath", ".jar");
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(jarFile));
ZipOutputStream jarPlugin = new JarOutputStream(out, manifest);
) {
jarFile.deleteOnExit();
}

return jarFile;
}

public static Collection<File> readClasspathManifest(File file) {
try (FileInputStream fis = new FileInputStream(file);
JarInputStream jarStream = new JarInputStream(fis);) {
Manifest mf = jarStream.getManifest();
Attributes att = mf.getMainAttributes();
String cp = att.getValue(Attributes.Name.CLASS_PATH);
String[] parts = cp.split("file:");
return Arrays.stream(parts)
.filter(part -> !part.isEmpty())
.map(part -> new File(part.trim()))
.collect(Collectors.toList());
} catch (IOException ex) {
throw new RuntimeException("Could not read classpath jar manifest", ex);
}
}
}
19 changes: 19 additions & 0 deletions pitest/src/test/java/org/pitest/util/ManifestUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.pitest.util;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.IOException;

import org.junit.Test;

public class ManifestUtilsTest {

@Test
public void shouldParseTheManifestsItCreates() throws IOException {
File actual = ManifestUtils.createClasspathJarFile("/some/path/foo.jar:/some/path/");
assertThat(ManifestUtils.readClasspathManifest(actual))
.containsExactly(new File("/some/path/foo.jar"), new File("/some/path/"));
}

}

0 comments on commit fab8ddf

Please sign in to comment.