1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 package org.apache.tools.ant.taskdefs.compilers;
20
21 import org.apache.tools.ant.BuildException;
22 import org.apache.tools.ant.Project;
23 import org.apache.tools.ant.Task;
24 import org.apache.tools.ant.types.Path;
25 import org.apache.tools.ant.util.ClasspathUtils;
26 import org.apache.tools.ant.util.JavaEnvUtils;
27
28 /**
29 * Creates the necessary compiler adapter, given basic criteria.
30 *
31 * @since Ant 1.3
32 */
33 public final class CompilerAdapterFactory {
34 private static final String MODERN_COMPILER = "com.sun.tools.javac.Main";
35
36 /** This is a singleton -- can't create instances!! */
37 private CompilerAdapterFactory() {
38 }
39
40 /**
41 * Based on the parameter passed in, this method creates the necessary
42 * factory desired.
43 *
44 * The current mapping for compiler names are as follows:
45 * <ul><li>jikes = jikes compiler
46 * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
47 * 1.1/1.2
48 * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
49 * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
50 * for Java / Visual J++
51 * <li>kjc = the kopi compiler</li>
52 * <li>gcj = the gcj compiler from gcc</li>
53 * <li>sj, symantec = the Symantec Java compiler</li>
54 * <li><i>a fully qualified classname</i> = the name of a compiler
55 * adapter
56 * </ul>
57 *
58 * @param compilerType either the name of the desired compiler, or the
59 * full classname of the compiler's adapter.
60 * @param task a task to log through.
61 * @return the compiler adapter
62 * @throws BuildException if the compiler type could not be resolved into
63 * a compiler adapter.
64 */
65 public static CompilerAdapter getCompiler(String compilerType, Task task)
66 throws BuildException {
67 return getCompiler(compilerType, task, null);
68 }
69
70 /**
71 * Based on the parameter passed in, this method creates the necessary
72 * factory desired.
73 *
74 * The current mapping for compiler names are as follows:
75 * <ul><li>jikes = jikes compiler
76 * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
77 * 1.1/1.2
78 * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
79 * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
80 * for Java / Visual J++
81 * <li>kjc = the kopi compiler</li>
82 * <li>gcj = the gcj compiler from gcc</li>
83 * <li>sj, symantec = the Symantec Java compiler</li>
84 * <li><i>a fully qualified classname</i> = the name of a compiler
85 * adapter
86 * </ul>
87 *
88 * @param compilerType either the name of the desired compiler, or the
89 * full classname of the compiler's adapter.
90 * @param task a task to log through.
91 * @param classpath the classpath to use when looking up an
92 * adapter class
93 * @return the compiler adapter
94 * @throws BuildException if the compiler type could not be resolved into
95 * a compiler adapter.
96 * @since Ant 1.8.0
97 */
98 public static CompilerAdapter getCompiler(String compilerType, Task task,
99 Path classpath)
100 throws BuildException {
101 if (compilerType.equalsIgnoreCase("jikes")) {
102 return new Jikes();
103 }
104 if (compilerType.equalsIgnoreCase("extjavac")) {
105 return new JavacExternal();
106 }
107 if (compilerType.equalsIgnoreCase("classic")
108 || compilerType.equalsIgnoreCase("javac1.1")
109 || compilerType.equalsIgnoreCase("javac1.2")) {
110 task.log("This version of java does "
111 + "not support the classic "
112 + "compiler; upgrading to modern",
113 Project.MSG_WARN);
114 compilerType = "modern";
115 }
116 //on java<=1.3 the modern falls back to classic if it is not found
117 //but on java>=1.4 we just bail out early
118 if (compilerType.equalsIgnoreCase("modern")
119 || compilerType.equalsIgnoreCase("javac1.3")
120 || compilerType.equalsIgnoreCase("javac1.4")
121 || compilerType.equalsIgnoreCase("javac1.5")
122 || compilerType.equalsIgnoreCase("javac1.6")) {
123 // does the modern compiler exist?
124 if (doesModernCompilerExist()) {
125 return new Javac13();
126 } else {
127 throw new BuildException("Unable to find a javac "
128 + "compiler;\n"
129 + MODERN_COMPILER
130 + " is not on the "
131 + "classpath.\n"
132 + "Perhaps JAVA_HOME does not"
133 + " point to the JDK.\n"
134 + "It is currently set to \""
135 + JavaEnvUtils.getJavaHome()
136 + "\"");
137 }
138 }
139
140 if (compilerType.equalsIgnoreCase("jvc")
141 || compilerType.equalsIgnoreCase("microsoft")) {
142 return new Jvc();
143 }
144 if (compilerType.equalsIgnoreCase("kjc")) {
145 return new Kjc();
146 }
147 if (compilerType.equalsIgnoreCase("gcj")) {
148 return new Gcj();
149 }
150 if (compilerType.equalsIgnoreCase("sj")
151 || compilerType.equalsIgnoreCase("symantec")) {
152 return new Sj();
153 }
154 return resolveClassName(compilerType,
155 // Memory-Leak in line below
156 task.getProject().createClassLoader(classpath));
157 }
158
159 /**
160 * query for the Modern compiler existing
161 * @return true if classic os on the classpath
162 */
163 private static boolean doesModernCompilerExist() {
164 try {
165 Class.forName(MODERN_COMPILER);
166 return true;
167 } catch (ClassNotFoundException cnfe) {
168 try {
169 ClassLoader cl = CompilerAdapterFactory.class.getClassLoader();
170 if (cl != null) {
171 cl.loadClass(MODERN_COMPILER);
172 return true;
173 }
174 } catch (ClassNotFoundException cnfe2) {
175 // Ignore Exception
176 }
177 }
178 return false;
179 }
180
181 /**
182 * Tries to resolve the given classname into a compiler adapter.
183 * Throws a fit if it can't.
184 *
185 * @param className The fully qualified classname to be created.
186 * @param loader the classloader to use
187 * @throws BuildException This is the fit that is thrown if className
188 * isn't an instance of CompilerAdapter.
189 */
190 private static CompilerAdapter resolveClassName(String className,
191 ClassLoader loader)
192 throws BuildException {
193 return (CompilerAdapter) ClasspathUtils.newInstance(className,
194 loader != null ? loader :
195 CompilerAdapterFactory.class.getClassLoader(),
196 CompilerAdapter.class);
197 }
198
199 }