View Javadoc

1   /*
2    * Copyright 2007 scala-tools.org
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing,
11   * software distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions
14   * and limitations under the License.
15   */
16  package org.scala_tools.maven;
17  
18  import java.util.HashSet;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.factory.ArtifactFactory;
25  import org.apache.maven.artifact.repository.ArtifactRepository;
26  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
27  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
28  import org.apache.maven.artifact.resolver.ArtifactResolver;
29  import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
30  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
31  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.plugin.AbstractMojo;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.MojoFailureException;
36  import org.apache.maven.project.MavenProject;
37  import org.apache.maven.project.MavenProjectBuilder;
38  import org.apache.maven.project.ProjectBuildingException;
39  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
40  import org.codehaus.plexus.util.StringUtils;
41  
42  abstract class ScalaMojoSupport extends AbstractMojo {
43  
44      public static final String SCALA_GROUPID= "org.scala-lang";
45      public static final String SCALA_LIBRARY_ARTIFACTID= "scala-library";
46      /**
47       * @parameter expression="${project}"
48       * @required
49       * @readonly
50       */
51      protected MavenProject project;
52  
53      /**
54       * Used to look up Artifacts in the remote repository.
55       *
56       * @parameter expression="${component.org.apache.maven.artifact.factory.ArtifactFactory}"
57       * @required
58       * @readonly
59       */
60      protected ArtifactFactory factory;
61  
62      /**
63       * Used to look up Artifacts in the remote repository.
64       *
65       * @parameter expression="${component.org.apache.maven.artifact.resolver.ArtifactResolver}"
66       * @required
67       * @readonly
68       */
69      protected ArtifactResolver resolver;
70      /**
71       * Location of the local repository.
72       *
73       * @parameter expression="${localRepository}"
74       * @readonly
75       * @required
76       */
77      protected ArtifactRepository localRepo;
78  
79      /**
80       * List of Remote Repositories used by the resolver
81       *
82       * @parameter expression="${project.remoteArtifactRepositories}"
83       * @readonly
84       * @required
85       */
86      protected List<?> remoteRepos;
87  
88      /**
89       * Additional dependencies/jar to add to classpath to run "scalaClassName" (scope and optional field not supported)
90       * ex :
91       *    &lt;dependencies>
92       *      &lt;dependency>
93       *        &lt;groupId>org.scala-tools&lt;/groupId>
94       *        &lt;artifactId>scala-compiler-addon&lt;/artifactId>
95       *        &lt;version>1.0-SNAPSHOT&lt;/version>
96       *      &lt;/dependency>
97       *    &lt;/dependencies>
98       * @parameter
99       */
100     protected BasicArtifact[] dependencies;
101 
102     /**
103      * Jvm Arguments.
104      *
105      * @parameter
106      */
107     protected String[] jvmArgs;
108 
109     /**
110      * compiler additionnals arguments
111      *
112      * @parameter
113      */
114     protected String[] args;
115 
116     /**
117      * className (FQN) of the scala tool to provide as
118      *
119      * @required
120      * @parameter expression="${maven.scala.className}"
121      *            default-value="scala.tools.nsc.Main"
122      */
123     protected String scalaClassName;
124 
125     /**
126      * Scala 's version to use
127      * @parameter expression="${maven.scala.version}"
128      */
129     protected String scalaVersion;
130 
131     /**
132      * Display the command line called ?
133      *
134      * @required
135      * @parameter expression="${maven.scala.displayCmd}"
136      *            default-value="false"
137      */
138     protected boolean displayCmd;
139 
140     /**
141      * Artifact factory, needed to download source jars.
142      *
143      * @component role="org.apache.maven.project.MavenProjectBuilder"
144      * @required
145      * @readonly
146      */
147     protected MavenProjectBuilder mavenProjectBuilder;
148 
149     /**
150      * This method resolves the dependency artifacts from the project.
151      *
152      * @param theProject The POM.
153      * @return resolved set of dependency artifacts.
154      *
155      * @throws ArtifactResolutionException
156      * @throws ArtifactNotFoundException
157      * @throws InvalidDependencyVersionException
158      */
159     @SuppressWarnings("unchecked")
160     protected Set<Artifact> resolveDependencyArtifacts(MavenProject theProject) throws Exception {
161         AndArtifactFilter filter = new AndArtifactFilter();
162         filter.add(new ScopeArtifactFilter(Artifact.SCOPE_TEST));
163         filter.add(new ArtifactFilter(){
164             public boolean include(Artifact artifact) {
165                 return !artifact.isOptional();
166             }
167         });
168         Set<Artifact> artifacts = theProject.createArtifacts(factory, Artifact.SCOPE_RUNTIME, filter);
169         for (Artifact artifact : artifacts) {
170             resolver.resolve(artifact, remoteRepos, localRepo);
171         }
172         return artifacts;
173     }
174 
175     /**
176      * This method resolves all transitive dependencies of an artifact.
177      *
178      * @param artifact the artifact used to retrieve dependencies
179      *
180      * @return resolved set of dependencies
181      *
182      * @throws ArtifactResolutionException
183      * @throws ArtifactNotFoundException
184      * @throws ProjectBuildingException
185      * @throws InvalidDependencyVersionException
186      */
187     protected Set<Artifact> resolveArtifactDependencies(Artifact artifact) throws Exception {
188         Artifact pomArtifact = factory.createArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(), "", "pom");
189         MavenProject pomProject = mavenProjectBuilder.buildFromRepository(pomArtifact, remoteRepos, localRepo);
190         return resolveDependencyArtifacts(pomProject);
191     }
192 
193     protected void addToClasspath(String groupId, String artifactId, String version, Set<String> classpath) throws Exception {
194         addToClasspath(factory.createArtifact(groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar"), classpath);
195     }
196 
197     protected void addToClasspath(Artifact artifact, Set<String> classpath) throws Exception {
198         resolver.resolve(artifact, remoteRepos, localRepo);
199         classpath.add(artifact.getFile().getCanonicalPath());
200         for(Artifact dep: resolveArtifactDependencies(artifact)) {
201             classpath.add(dep.getFile().getCanonicalPath());
202         }
203     }
204 
205     public void execute() throws MojoExecutionException, MojoFailureException {
206         try {
207             checkScalaVersion();
208             doExecute();
209         } catch (MojoExecutionException exc) {
210             throw exc;
211         } catch (MojoFailureException exc) {
212             throw exc;
213         } catch (RuntimeException exc) {
214             throw exc;
215         } catch (Exception exc) {
216             throw new MojoExecutionException("wrap: " + exc, exc);
217         }
218     }
219 
220     @SuppressWarnings("unchecked")
221     protected List<Dependency> getDependencies() {
222         return project.getCompileDependencies();
223     }
224 
225     @SuppressWarnings("unchecked")
226     protected void checkScalaVersion() throws Exception {
227         String detectedScalaVersion = null;
228         for (Iterator it = getDependencies().iterator(); it.hasNext();) {
229             Dependency dep = (Dependency) it.next();
230             if (SCALA_GROUPID.equals(dep.getGroupId()) && SCALA_LIBRARY_ARTIFACTID.equals(dep.getArtifactId())) {
231                 detectedScalaVersion = dep.getVersion();
232             }
233         }
234         if (StringUtils.isEmpty(detectedScalaVersion)) {
235             getLog().warn("you don't define "+SCALA_GROUPID + ":" + SCALA_LIBRARY_ARTIFACTID + " as a dependency of the project");
236         } else {
237             if (StringUtils.isNotEmpty(scalaVersion)) {
238                 if (!scalaVersion.equals(detectedScalaVersion)) {
239                     getLog().warn("scala library version define in dependencies doesn't match the scalaVersion of the plugin");
240                 }
241                 getLog().info("suggestion: remove the scalaVersion from pom.xml");
242             } else {
243                 scalaVersion = detectedScalaVersion;
244             }
245         }
246         if (StringUtils.isEmpty(scalaVersion)) {
247             throw new MojoFailureException("no scalaVersion detected or set");
248         }
249     }
250     
251     protected abstract void doExecute() throws Exception;
252 
253     protected JavaCommand getScalaCommand() throws Exception {
254         JavaCommand cmd = getEmptyScalaCommand(scalaClassName);
255         cmd.addArgs(args);
256         cmd.addJvmArgs(jvmArgs);
257         return cmd;
258     }
259 
260     protected JavaCommand getEmptyScalaCommand(String mainClass) throws Exception {
261         JavaCommand cmd = new JavaCommand(this, mainClass, getToolClasspath(), null, null);
262         cmd.addJvmArgs("-Xbootclasspath/a:"+ getBootClasspath());
263         return cmd;
264     }
265 
266     private String getToolClasspath() throws Exception {
267         Set<String> classpath = new HashSet<String>();
268         addToClasspath(SCALA_GROUPID, "scala-compiler", scalaVersion, classpath);
269 //        addToClasspath(SCALA_GROUPID, "scala-decoder", scalaVersion, classpath);
270 //        addToClasspath(SCALA_GROUPID, "scala-dbc", scalaVersion, classpath);
271         if (dependencies != null) {
272             for(BasicArtifact artifact: dependencies) {
273                 addToClasspath(artifact.groupId, artifact.artifactId, artifact.version, classpath);
274             }
275         }
276         return JavaCommand.toMultiPath(classpath.toArray(new String[classpath.size()]));
277     }
278 
279     private String getBootClasspath() throws Exception {
280         Set<String> classpath = new HashSet<String>();
281         addToClasspath(SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, scalaVersion, classpath);
282         return JavaCommand.toMultiPath(classpath.toArray(new String[classpath.size()]));
283     }
284 
285 	/**
286      * @return
287      *           This returns whether or not the scala version can support having java sent into the compiler
288      */
289 	protected boolean isJavaSupportedByCompiler() {
290 		return new VersionNumber(scalaVersion).compareTo(new VersionNumber("2.7.2")) >= 0;
291 	}
292 
293 }