Skip to content

Commit c0d0a92

Browse files
author
Marco Hunsicker
committed
Manage test dependencies with Bazel
1 parent a2ecfdc commit c0d0a92

File tree

13 files changed

+370
-116
lines changed

13 files changed

+370
-116
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package org.antlr.bazel;
2+
3+
import java.io.BufferedReader;
4+
import java.io.IOException;
5+
import java.io.InputStreamReader;
6+
import java.nio.file.FileVisitResult;
7+
import java.nio.file.Files;
8+
import java.nio.file.LinkOption;
9+
import java.nio.file.Path;
10+
import java.nio.file.PathMatcher;
11+
import java.nio.file.Paths;
12+
import java.nio.file.SimpleFileVisitor;
13+
import java.nio.file.attribute.BasicFileAttributes;
14+
import java.util.ArrayList;
15+
import java.util.Collection;
16+
import java.util.Collections;
17+
import java.util.HashMap;
18+
import java.util.Map;
19+
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertFalse;
22+
import static org.junit.Assert.assertTrue;
23+
24+
25+
/**
26+
* Fetches dependencies.
27+
*
28+
* @author Marco Hunsicker
29+
*/
30+
class Dependencies
31+
{
32+
private static final Map<Version, String[]> DEPENDENCIES = download();
33+
34+
/**
35+
* Returns the ANTLR 4 dependencies.
36+
*
37+
* @return the ANTLR 4 dependencies.
38+
*/
39+
public static String[] antlr2()
40+
{
41+
return DEPENDENCIES.get(Version.V2);
42+
}
43+
44+
45+
/**
46+
* Returns the ANTLR 3 dependencies.
47+
*
48+
* @return the ANTLR 3 dependencies.
49+
*/
50+
public static String[] antlr3()
51+
{
52+
return DEPENDENCIES.get(Version.V3);
53+
}
54+
55+
56+
/**
57+
* Returns the ANTLR 2 dependencies.
58+
*
59+
* @return the ANTLR 2 dependencies.
60+
*/
61+
public static String[] antlr4()
62+
{
63+
return DEPENDENCIES.get(Version.V4);
64+
}
65+
66+
67+
private static Map<Version, String[]> download()
68+
{
69+
try
70+
{
71+
// the Bazel workspace root we use for dependency loading
72+
Path workspace = Paths.get("src/it/resources/antlr/DownloadDependencies")
73+
.toRealPath();
74+
assertTrue(Files.exists(workspace));
75+
76+
Path base = getBaseDirectory(workspace);
77+
78+
Map<Version, String[]> deps = loadDependencies(base);
79+
80+
// if short-circuiting did not work, fetch the dependencies
81+
if (deps.isEmpty())
82+
{
83+
fetchDependencies(workspace, base);
84+
deps = loadDependencies(base);
85+
}
86+
87+
assertFalse(deps.isEmpty());
88+
89+
return deps;
90+
}
91+
catch (Exception ex)
92+
{
93+
throw new IllegalStateException("Failed to load dependencies", ex);
94+
}
95+
}
96+
97+
98+
private static void fetchDependencies(Path workspace, Path base) throws Exception
99+
{
100+
Process p = new ProcessBuilder().command("bazel", "fetch", "//...")
101+
.directory(Paths.get(".").toRealPath().toFile())
102+
.inheritIO()
103+
.directory(workspace.toFile())
104+
.start();
105+
p.waitFor();
106+
107+
if (p.exitValue() != 0)
108+
{
109+
// cleanup if something went wrong to make short-circuiting reliable
110+
Path external = base.resolve("external");
111+
112+
if (Files.exists(base))
113+
{
114+
Disk.delete(external);
115+
}
116+
}
117+
118+
assertEquals(0, p.exitValue());
119+
}
120+
121+
122+
private static Path getBaseDirectory(Path root) throws Exception
123+
{
124+
Process p = new ProcessBuilder().command("bazel", "info", "output_base")
125+
.directory(Paths.get(".").toRealPath().toFile())
126+
.redirectErrorStream(true)
127+
.directory(root.toFile())
128+
.start();
129+
130+
try (BufferedReader reader = new BufferedReader(
131+
new InputStreamReader(p.getInputStream())))
132+
{
133+
StringBuilder buf = new StringBuilder();
134+
String line = null;
135+
136+
while ((line = reader.readLine()) != null)
137+
{
138+
buf.append(line);
139+
}
140+
141+
Path base = Paths.get(buf.toString());
142+
assertTrue(Files.exists(base));
143+
144+
return base;
145+
}
146+
finally
147+
{
148+
p.waitFor();
149+
assertEquals(0, p.exitValue());
150+
}
151+
}
152+
153+
154+
private static Map<Version, String[]> loadDependencies(final Path base)
155+
throws IOException
156+
{
157+
final Collection<String> deps2 = new ArrayList<>();
158+
final Collection<String> deps3 = new ArrayList<>();
159+
final Collection<String> deps4 = new ArrayList<>();
160+
161+
Path external = base.resolve("external");
162+
163+
if (Files.exists(external))
164+
{
165+
Files.walkFileTree(external, new SimpleFileVisitor<Path>()
166+
{
167+
PathMatcher archives = base.getFileSystem()
168+
.getPathMatcher("glob:**/*.jar");
169+
PathMatcher antlr4 = base.getFileSystem()
170+
.getPathMatcher(
171+
"regex:.*(antlr4|antlr4-runtime|antlr-runtime|ST4|javax.json)-.*.jar");
172+
PathMatcher antlr3 = base.getFileSystem()
173+
.getPathMatcher("regex:.*/(antlr-3|antlr-runtime|ST4).*.jar");
174+
PathMatcher antlr2 = base.getFileSystem()
175+
.getPathMatcher("glob:**/antlr-2*.jar");
176+
177+
@Override
178+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
179+
throws IOException
180+
{
181+
if (archives.matches(file)
182+
&& Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS))
183+
{
184+
if (antlr4.matches(file))
185+
{
186+
deps4.add(file.toString());
187+
}
188+
189+
if (antlr3.matches(file))
190+
{
191+
deps3.add(file.toString());
192+
}
193+
194+
if (antlr2.matches(file))
195+
{
196+
deps2.add(file.toString());
197+
}
198+
}
199+
200+
return super.visitFile(file, attrs);
201+
}
202+
});
203+
204+
Map<Version, String[]> result = new HashMap<>();
205+
result.put(Version.V2, deps2.toArray(new String[deps2.size()]));
206+
result.put(Version.V3, deps3.toArray(new String[deps3.size()]));
207+
result.put(Version.V4, deps4.toArray(new String[deps4.size()]));
208+
209+
return result;
210+
}
211+
212+
return Collections.emptyMap();
213+
}
214+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package org.antlr.bazel;
2+
3+
import java.io.IOException;
4+
import java.nio.file.CopyOption;
5+
import java.nio.file.FileVisitResult;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.SimpleFileVisitor;
9+
import java.nio.file.StandardCopyOption;
10+
import java.nio.file.attribute.BasicFileAttributes;
11+
12+
13+
/**
14+
* Disk helper.
15+
*
16+
* @author Marco Hunsicker
17+
*/
18+
class Disk
19+
{
20+
private static final CopyOption[] ATTRIBUTES =
21+
{ StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING };
22+
23+
/** Creates a new Disk object. */
24+
private Disk()
25+
{
26+
super();
27+
}
28+
29+
/**
30+
* Copies the given file or directory to the given destination.
31+
*
32+
* @param path the file or directory.
33+
* @param target the target.
34+
* @param options the options.
35+
*
36+
* @throws IOException if an I/O error occurred.
37+
*/
38+
public static void copy(final Path path, final Path target, CopyOption... options)
39+
throws IOException
40+
{
41+
final CopyOption[] opt = (options.length == 0) ? ATTRIBUTES : options;
42+
43+
if (Files.isDirectory(path))
44+
{
45+
Files.walkFileTree(path, new SimpleFileVisitor<Path>()
46+
{
47+
@Override
48+
public FileVisitResult preVisitDirectory(Path dir,
49+
BasicFileAttributes attrs) throws IOException
50+
{
51+
Files.createDirectories(
52+
target.resolve(path.relativize(dir).toString()));
53+
54+
return FileVisitResult.CONTINUE;
55+
}
56+
57+
58+
@Override
59+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
60+
throws IOException
61+
{
62+
Files.copy(file,
63+
target.resolve(path.relativize(file).toString()),
64+
opt);
65+
66+
return FileVisitResult.CONTINUE;
67+
}
68+
});
69+
}
70+
else
71+
{
72+
Files.copy(path, target, opt);
73+
}
74+
}
75+
76+
77+
/**
78+
* Deletes the given file or directory.
79+
*
80+
* @param path the path.
81+
*
82+
* @throws IOException if an I/O error occurred.
83+
*/
84+
public static void delete(Path path) throws IOException
85+
{
86+
if (Files.isDirectory(path))
87+
{
88+
Files.walkFileTree(path, DeleteVisitor.INSTANCE);
89+
}
90+
else if (Files.exists(path))
91+
{
92+
Files.delete(path);
93+
}
94+
}
95+
96+
private static class DeleteVisitor extends SimpleFileVisitor<Path>
97+
{
98+
public static final DeleteVisitor INSTANCE = new DeleteVisitor();
99+
100+
@Override
101+
public FileVisitResult postVisitDirectory(Path dir, IOException cause)
102+
throws IOException
103+
{
104+
Files.delete(dir);
105+
106+
return FileVisitResult.CONTINUE;
107+
}
108+
109+
110+
@Override
111+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
112+
throws IOException
113+
{
114+
Files.delete(file);
115+
116+
return FileVisitResult.CONTINUE;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)