1 package auxtestlib;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.List;
6
7 import auxtestlib.CommandRunner.CommandOutput;
8
9 /**
10 * Class that runs javac and compiles source code. Javac is run as an external
11 * process.
12 */
13 public final class JavacRunner {
14 /**
15 * Utility class: no constructor.
16 */
17 private JavacRunner() {
18 /*
19 * Nothing to do.
20 */
21 }
22
23 /**
24 * Runs javac.
25 *
26 * @param dir the directory where javac should run
27 * @param source file with source code
28 *
29 * @throws Exception failed to compile
30 */
31 public static void javac(File dir, String source) throws Exception {
32 javac(dir, source, null);
33 }
34
35 /**
36 * Runs javac.
37 *
38 * @param dir the directory where javac should run
39 * @param source file with source code
40 * @param cp additional classpath (<code>null</code> not to add any
41 * additional classpath). If this parameter is defined, any position in the
42 * array which is <code>null</code> is replaced by the application's current
43 * classpath. Inside this array both strings or files can be placed. Files
44 * will be added to the classpath as absolute paths.
45 *
46 * @throws Exception compilation failed
47 */
48 public static void javac(File dir, String source, Object cp[])
49 throws Exception {
50 javac(dir, new String[] { source }, cp);
51 }
52
53 /**
54 * Creates the additional classpath to run javac.
55 *
56 * @param classpath additional classpath (<code>null</code> not to add any
57 * additional classpath). If this parameter is defined, any position in the
58 * array which is <code>null</code> is replaced by the application's current
59 * classpath. Inside this array both strings or files can be placed. Files
60 * will be added to the classpath as absolute paths.
61 *
62 * @return the classpath
63 *
64 * @throws Exception failed to add the classpath
65 */
66 private static String buildAdditionalClasspath(Object[] classpath)
67 throws Exception {
68 Object[] cp = classpath;
69
70 if (cp == null || cp.length == 0) {
71 cp = new String[] { null };
72 }
73
74 StringBuffer cpb = new StringBuffer();
75 for (int i = 0; i < cp.length; i++) {
76 if (i != 0) {
77 cpb.append(File.pathSeparator);
78 }
79
80 if (cp[i] == null) {
81 cpb.append(System.getProperty("java.class.path"));
82 } else {
83 if (cp[i] instanceof String) {
84 cpb.append(cp[i]);
85 } else if (cp[i] instanceof File) {
86 cpb.append(((File) cp[i]).getAbsolutePath());
87 } else {
88 throw new IllegalArgumentException("Path must "
89 + "contain only strings or files.");
90 }
91 }
92 }
93
94 return cpb.toString();
95 }
96
97 /**
98 * Runs javac.
99 *
100 * @param dir directory where javac should be run
101 * @param sources files with source code. It can be <code>null</code> if
102 * there are no files to compile
103 * @param cp additional classpath (<code>null</code> not to add any
104 * additional classpath). If this parameter is defined, any position in the
105 * array which is <code>null</code> is replaced by the application's current
106 * classpath. Inside this array both strings or files can be placed. Files
107 * will be added to the classpath as absolute paths.
108 *
109 * @throws Exception failed to compile
110 */
111 public static void javac(File dir, String[] sources, Object[] cp)
112 throws Exception {
113
114 // Leave if there is nothing to compile.
115 if (sources == null || sources.length == 0) {
116 return;
117 }
118
119 String cpb = buildAdditionalClasspath(cp);
120 CommandRunner cr = new CommandRunner();
121
122 List<String> cmdList = new ArrayList<>();
123 cmdList.add("javac");
124 cmdList.add("-d");
125 cmdList.add(".");
126 cmdList.add("-cp");
127 cmdList.add(cpb);
128 for (int i = 0; i < sources.length; i++) {
129 cmdList.add(sources[i]);
130 }
131
132 String[] cmds = cmdList.toArray(new String[cmdList.size()]);
133 CommandOutput co = cr.run_command(cmds, dir, 60);
134 if (co == null || co.exitCode != 0) {
135 StringBuffer cmdsStr = new StringBuffer();
136 cmdsStr.append('{');
137 for (int i = 0; i < cmds.length; i++) {
138 if (i != 0) {
139 cmdsStr.append(';');
140 }
141
142 cmdsStr.append('\'');
143 cmdsStr.append(cmds[i]);
144 cmdsStr.append('\'');
145 }
146
147 cmdsStr.append('}');
148
149 StringBuffer extra = new StringBuffer();
150 if (co != null) {
151 extra.append("; stdout:\n");
152 extra.append(co.output);
153 extra.append("\nstderr:\n");
154 extra.append(co.error);
155 }
156
157 throw new CommandExecutionException("Failed running " + cmdsStr
158 + ", @ " + dir.getAbsolutePath() + extra);
159 }
160 }
161
162 /**
163 * Compiles a file which exists as a resource.
164 *
165 * @param dir directory where javac should be run
166 * @param resource the resource to compile
167 * @param cls the name of the class
168 * @param cp additional classpath (<code>null</code> not to add any
169 * additional classpath). If this parameter is defined, any position in the
170 * array which is <code>null</code> is replaced by the application's current
171 * classpath. Inside this array both strings or files can be placed. Files
172 * will be added to the classpath as absolute paths.
173 *
174 * @return the directory with the first "package".
175 *
176 * @throws Exception failed to compile
177 */
178 public static File javacResource(File dir, String resource, String cls,
179 Object[] cp) throws Exception {
180 return javacResources(dir, new String[] { resource },
181 new String[] { cls }, cp);
182 }
183
184 /**
185 * Compiles files which exists as resources.
186 *
187 * @param dir directory where javac should be run
188 * @param resources the resources to compile
189 * @param cls the name of the classes
190 * @param cp additional classpath (<code>null</code> not to add any
191 * additional classpath). If this parameter is defined, any position in the
192 * array which is <code>null</code> is replaced by the application's current
193 * classpath. Inside this array both strings or files can be placed. Files
194 * will be added to the classpath as absolute paths.
195 *
196 * @return the directory with the first "package".
197 *
198 * @throws Exception failed to compile
199 */
200 public static File javacResources(File dir, String[] resources,
201 String[] cls, Object[] cp) throws Exception {
202 String classes[] = new String[cls.length];
203 File subdir = null;
204
205 // LEts break the name by "." because we want the directories.
206 for (int j = 0; j < cls.length; j++) {
207 String parts[] = cls[j].split("\\.");
208
209 if (parts.length < 2) {
210 throw new IllegalArgumentException("The class to compile "
211 + "must have at least one package.");
212 }
213
214 // We create all directories.
215 File files[] = new File[parts.length];
216 for (int i = 0; i < files.length - 1; i++) {
217 files[i] = new File(i == 0 ? dir : files[i - 1], parts[i]);
218 boolean res = files[i].mkdir();
219 assert res || !res; // Otherwise findbugs complains.
220 }
221
222 subdir = files[0];
223
224 // Create a file that represents .java.
225 int pos = parts.length - 1;
226 files[pos] = new File(files[pos - 1], parts[pos] + ".java");
227
228 // We define the file contents.
229 FileContentWorker.set_contents_bin(files[pos], FileContentWorker
230 .read_resource_bin(resources[j]));
231 classes[j] = cls[j].replace('.', '/') + ".java";
232 }
233
234 // Compile.
235 JavacRunner.javac(dir, classes, cp);
236
237 return subdir;
238 }
239 }