Skip to content

Commit

Permalink
Manage test dependencies with Bazel
Browse files Browse the repository at this point in the history
  • Loading branch information
Marco Hunsicker committed Apr 4, 2018
1 parent a2ecfdc commit c0d0a92
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 116 deletions.
214 changes: 214 additions & 0 deletions src/it/java/org/antlr/bazel/Dependencies.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package org.antlr.bazel;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;


/**
* Fetches dependencies.
*
* @author Marco Hunsicker
*/
class Dependencies
{
private static final Map<Version, String[]> DEPENDENCIES = download();

/**
* Returns the ANTLR 4 dependencies.
*
* @return the ANTLR 4 dependencies.
*/
public static String[] antlr2()
{
return DEPENDENCIES.get(Version.V2);
}


/**
* Returns the ANTLR 3 dependencies.
*
* @return the ANTLR 3 dependencies.
*/
public static String[] antlr3()
{
return DEPENDENCIES.get(Version.V3);
}


/**
* Returns the ANTLR 2 dependencies.
*
* @return the ANTLR 2 dependencies.
*/
public static String[] antlr4()
{
return DEPENDENCIES.get(Version.V4);
}


private static Map<Version, String[]> download()
{
try
{
// the Bazel workspace root we use for dependency loading
Path workspace = Paths.get("src/it/resources/antlr/DownloadDependencies")
.toRealPath();
assertTrue(Files.exists(workspace));

Path base = getBaseDirectory(workspace);

Map<Version, String[]> deps = loadDependencies(base);

// if short-circuiting did not work, fetch the dependencies
if (deps.isEmpty())
{
fetchDependencies(workspace, base);
deps = loadDependencies(base);
}

assertFalse(deps.isEmpty());

return deps;
}
catch (Exception ex)
{
throw new IllegalStateException("Failed to load dependencies", ex);
}
}


private static void fetchDependencies(Path workspace, Path base) throws Exception
{
Process p = new ProcessBuilder().command("bazel", "fetch", "//...")
.directory(Paths.get(".").toRealPath().toFile())
.inheritIO()
.directory(workspace.toFile())
.start();
p.waitFor();

if (p.exitValue() != 0)
{
// cleanup if something went wrong to make short-circuiting reliable
Path external = base.resolve("external");

if (Files.exists(base))
{
Disk.delete(external);
}
}

assertEquals(0, p.exitValue());
}


private static Path getBaseDirectory(Path root) throws Exception
{
Process p = new ProcessBuilder().command("bazel", "info", "output_base")
.directory(Paths.get(".").toRealPath().toFile())
.redirectErrorStream(true)
.directory(root.toFile())
.start();

try (BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream())))
{
StringBuilder buf = new StringBuilder();
String line = null;

while ((line = reader.readLine()) != null)
{
buf.append(line);
}

Path base = Paths.get(buf.toString());
assertTrue(Files.exists(base));

return base;
}
finally
{
p.waitFor();
assertEquals(0, p.exitValue());
}
}


private static Map<Version, String[]> loadDependencies(final Path base)
throws IOException
{
final Collection<String> deps2 = new ArrayList<>();
final Collection<String> deps3 = new ArrayList<>();
final Collection<String> deps4 = new ArrayList<>();

Path external = base.resolve("external");

if (Files.exists(external))
{
Files.walkFileTree(external, new SimpleFileVisitor<Path>()
{
PathMatcher archives = base.getFileSystem()
.getPathMatcher("glob:**/*.jar");
PathMatcher antlr4 = base.getFileSystem()
.getPathMatcher(
"regex:.*(antlr4|antlr4-runtime|antlr-runtime|ST4|javax.json)-.*.jar");
PathMatcher antlr3 = base.getFileSystem()
.getPathMatcher("regex:.*/(antlr-3|antlr-runtime|ST4).*.jar");
PathMatcher antlr2 = base.getFileSystem()
.getPathMatcher("glob:**/antlr-2*.jar");

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
if (archives.matches(file)
&& Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS))
{
if (antlr4.matches(file))
{
deps4.add(file.toString());
}

if (antlr3.matches(file))
{
deps3.add(file.toString());
}

if (antlr2.matches(file))
{
deps2.add(file.toString());
}
}

return super.visitFile(file, attrs);
}
});

Map<Version, String[]> result = new HashMap<>();
result.put(Version.V2, deps2.toArray(new String[deps2.size()]));
result.put(Version.V3, deps3.toArray(new String[deps3.size()]));
result.put(Version.V4, deps4.toArray(new String[deps4.size()]));

return result;
}

return Collections.emptyMap();
}
}
119 changes: 119 additions & 0 deletions src/it/java/org/antlr/bazel/Disk.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.antlr.bazel;

import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;


/**
* Disk helper.
*
* @author Marco Hunsicker
*/
class Disk
{
private static final CopyOption[] ATTRIBUTES =
{ StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING };

/** Creates a new Disk object. */
private Disk()
{
super();
}

/**
* Copies the given file or directory to the given destination.
*
* @param path the file or directory.
* @param target the target.
* @param options the options.
*
* @throws IOException if an I/O error occurred.
*/
public static void copy(final Path path, final Path target, CopyOption... options)
throws IOException
{
final CopyOption[] opt = (options.length == 0) ? ATTRIBUTES : options;

if (Files.isDirectory(path))
{
Files.walkFileTree(path, new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException
{
Files.createDirectories(
target.resolve(path.relativize(dir).toString()));

return FileVisitResult.CONTINUE;
}


@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.copy(file,
target.resolve(path.relativize(file).toString()),
opt);

return FileVisitResult.CONTINUE;
}
});
}
else
{
Files.copy(path, target, opt);
}
}


/**
* Deletes the given file or directory.
*
* @param path the path.
*
* @throws IOException if an I/O error occurred.
*/
public static void delete(Path path) throws IOException
{
if (Files.isDirectory(path))
{
Files.walkFileTree(path, DeleteVisitor.INSTANCE);
}
else if (Files.exists(path))
{
Files.delete(path);
}
}

private static class DeleteVisitor extends SimpleFileVisitor<Path>
{
public static final DeleteVisitor INSTANCE = new DeleteVisitor();

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException cause)
throws IOException
{
Files.delete(dir);

return FileVisitResult.CONTINUE;
}


@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
Files.delete(file);

return FileVisitResult.CONTINUE;
}
}
}
Loading

0 comments on commit c0d0a92

Please sign in to comment.