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.util.ClasspathUtils;
25 import org.apache.tools.ant.util.JavaEnvUtils;
26
27 /**
28 * Creates the necessary compiler adapter, given basic criteria.
29 *
30 * @since Ant 1.3
31 */
32 public final class CompilerAdapterFactory {
33 private static final String MODERN_COMPILER = "com.sun.tools.javac.Main";
34
35 /** This is a singleton -- can't create instances!! */
36 private CompilerAdapterFactory() {
37 }
38
39 /**
40 * Based on the parameter passed in, this method creates the necessary
41 * factory desired.
42 *
43 * The current mapping for compiler names are as follows:
44 * <ul><li>jikes = jikes compiler
45 * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
46 * 1.1/1.2
47 * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
48 * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
49 * for Java / Visual J++
50 * <li>kjc = the kopi compiler</li>
51 * <li>gcj = the gcj compiler from gcc</li>
52 * <li>sj, symantec = the Symantec Java compiler</li>
53 * <li><i>a fully qualified classname</i> = the name of a compiler
54 * adapter
55 * </ul>
56 *
57 * @param compilerType either the name of the desired compiler, or the
58 * full classname of the compiler's adapter.
59 * @param task a task to log through.
60 * @return the compiler adapter
61 * @throws BuildException if the compiler type could not be resolved into
62 * a compiler adapter.
63 */
64 public static CompilerAdapter getCompiler(String compilerType, Task task)
65 throws BuildException {
66 boolean isClassicCompilerSupported = true;
67 //as new versions of java come out, add them to this test
68 if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
69 && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
70 isClassicCompilerSupported = false;
71 }
72
73 if (compilerType.equalsIgnoreCase("jikes")) {
74 return new Jikes();
75 }
76 if (compilerType.equalsIgnoreCase("extJavac")) {
77 return new JavacExternal();
78 }
79 if (compilerType.equalsIgnoreCase("classic")
80 || compilerType.equalsIgnoreCase("javac1.1")
81 || compilerType.equalsIgnoreCase("javac1.2")) {
82 if (isClassicCompilerSupported) {
83 return new Javac12();
84 } else {
85 task.log("This version of java does "
86 + "not support the classic "
87 + "compiler; upgrading to modern",
88 Project.MSG_WARN);
89 compilerType = "modern";
90 }
91 }
92 //on java<=1.3 the modern falls back to classic if it is not found
93 //but on java>=1.4 we just bail out early
94 if (compilerType.equalsIgnoreCase("modern")
95 || compilerType.equalsIgnoreCase("javac1.3")
96 || compilerType.equalsIgnoreCase("javac1.4")
97 || compilerType.equalsIgnoreCase("javac1.5")
98 || compilerType.equalsIgnoreCase("javac1.6")) {
99 // does the modern compiler exist?
100 if (doesModernCompilerExist()) {
101 return new Javac13();
102 } else {
103 if (isClassicCompilerSupported) {
104 task.log("Modern compiler not found - looking for "
105 + "classic compiler", Project.MSG_WARN);
106 return new Javac12();
107 } else {
108 throw new BuildException("Unable to find a javac "
109 + "compiler;\n"
110 + MODERN_COMPILER
111 + " is not on the "
112 + "classpath.\n"
113 + "Perhaps JAVA_HOME does not"
114 + " point to the JDK.\n"
115 + "It is currently set to \""
116 + JavaEnvUtils.getJavaHome()
117 + "\"");
118 }
119 }
120 }
121
122 if (compilerType.equalsIgnoreCase("jvc")
123 || compilerType.equalsIgnoreCase("microsoft")) {
124 return new Jvc();
125 }
126 if (compilerType.equalsIgnoreCase("kjc")) {
127 return new Kjc();
128 }
129 if (compilerType.equalsIgnoreCase("gcj")) {
130 return new Gcj();
131 }
132 if (compilerType.equalsIgnoreCase("sj")
133 || compilerType.equalsIgnoreCase("symantec")) {
134 return new Sj();
135 }
136 return resolveClassName(compilerType);
137 }
138
139 /**
140 * query for the Modern compiler existing
141 * @return true if classic os on the classpath
142 */
143 private static boolean doesModernCompilerExist() {
144 try {
145 Class.forName(MODERN_COMPILER);
146 return true;
147 } catch (ClassNotFoundException cnfe) {
148 try {
149 ClassLoader cl = CompilerAdapterFactory.class.getClassLoader();
150 if (cl != null) {
151 cl.loadClass(MODERN_COMPILER);
152 return true;
153 }
154 } catch (ClassNotFoundException cnfe2) {
155 // Ignore Exception
156 }
157 }
158 return false;
159 }
160
161 /**
162 * Tries to resolve the given classname into a compiler adapter.
163 * Throws a fit if it can't.
164 *
165 * @param className The fully qualified classname to be created.
166 * @throws BuildException This is the fit that is thrown if className
167 * isn't an instance of CompilerAdapter.
168 */
169 private static CompilerAdapter resolveClassName(String className)
170 throws BuildException {
171 return (CompilerAdapter) ClasspathUtils.newInstance(className,
172 CompilerAdapterFactory.class.getClassLoader(),
173 CompilerAdapter.class);
174 }
175
176 }