001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2013 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
019 */
020package org.sonar.maven;
021
022import org.apache.maven.artifact.factory.ArtifactFactory;
023import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
024import org.apache.maven.artifact.repository.ArtifactRepository;
025import org.apache.maven.artifact.resolver.ArtifactCollector;
026import org.apache.maven.artifact.versioning.ArtifactVersion;
027import org.apache.maven.execution.MavenSession;
028import org.apache.maven.execution.RuntimeInformation;
029import org.apache.maven.lifecycle.LifecycleExecutor;
030import org.apache.maven.plugin.AbstractMojo;
031import org.apache.maven.plugin.MojoExecutionException;
032import org.apache.maven.plugin.MojoFailureException;
033import org.apache.maven.project.MavenProject;
034import org.apache.maven.project.MavenProjectBuilder;
035import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
036import org.sonar.runner.api.EmbeddedRunner;
037import org.sonar.runner.api.RunnerProperties;
038import org.sonar.runner.api.ScanProperties;
039
040import java.io.File;
041import java.io.IOException;
042
043/**
044 * @goal sonar
045 * @aggregator
046 * @requiresDependencyResolution test
047 */
048public final class SonarMojo extends AbstractMojo {
049
050  /**
051   * @parameter expression="${session}"
052   * @required
053   * @readonly
054   */
055  private MavenSession session;
056
057  /**
058   * @parameter expression="${project}"
059   * @required
060   * @readonly
061   */
062  private MavenProject project;
063
064  /**
065   * @component
066   * @required
067   */
068  private LifecycleExecutor lifecycleExecutor;
069
070  /**
071   * The artifact factory to use.
072   *
073   * @component
074   * @required
075   * @readonly
076   */
077  private ArtifactFactory artifactFactory;
078
079  /**
080   * The artifact repository to use.
081   *
082   * @parameter expression="${localRepository}"
083   * @required
084   * @readonly
085   */
086  private ArtifactRepository localRepository;
087
088  /**
089   * The artifact metadata source to use.
090   *
091   * @component
092   * @required
093   * @readonly
094   */
095  private ArtifactMetadataSource artifactMetadataSource;
096
097  /**
098   * The artifact collector to use.
099   *
100   * @component
101   * @required
102   * @readonly
103   */
104  private ArtifactCollector artifactCollector;
105
106  /**
107   * The dependency tree builder to use.
108   *
109   * @component
110   * @required
111   * @readonly
112   */
113  private DependencyTreeBuilder dependencyTreeBuilder;
114
115  /**
116   * @component
117   * @required
118   * @readonly
119   */
120  private MavenProjectBuilder projectBuilder;
121
122  /**
123   * @component
124   * @required
125   * @readonly
126   * @VisibleForTesting
127   */
128  RuntimeInformation runtimeInformation;
129
130  public void execute() throws MojoExecutionException, MojoFailureException {
131    ArtifactVersion mavenVersion = getMavenVersion();
132    if (mavenVersion.getMajorVersion() == 2 && mavenVersion.getMinorVersion() < 2) {
133      throw new MojoExecutionException("Please use at least Maven 2.2.x to perform SonarQube analysis (current version is " + mavenVersion.toString() + ")");
134    }
135
136    EmbeddedRunner runner = EmbeddedRunner.create()
137        .setApp("Maven", mavenVersion.toString())
138        .addProperties(session.getExecutionProperties())
139        .addProperties(project.getModel().getProperties())
140        // Add user properties (ie command line arguments -Dsonar.xxx=yyyy) in last position to override all other
141        .addProperties(session.getUserProperties());
142    String encoding = getSourceEncoding(project);
143    if (encoding != null) {
144      runner.setProperty(ScanProperties.PROJECT_SOURCE_ENCODING, encoding);
145    }
146    runner
147        .setProperty(ScanProperties.PROJECT_KEY, getSonarKey(project))
148        .setProperty(RunnerProperties.WORK_DIR, getSonarWorkDir(project).getAbsolutePath())
149        .setProperty(ScanProperties.PROJECT_BASEDIR, project.getBasedir().getAbsolutePath())
150        .setProperty(ScanProperties.PROJECT_VERSION, toString(project.getVersion()))
151        .setProperty(ScanProperties.PROJECT_NAME, toString(project.getName()))
152        .setProperty(ScanProperties.PROJECT_DESCRIPTION, toString(project.getDescription()))
153        .setProperty(ScanProperties.PROJECT_SOURCE_DIRS, ".");
154    // Exclude log implementation to not conflict with Maven 3.1 logging impl
155    runner.mask("org.slf4j.LoggerFactory")
156        // Include slf4j Logger that is exposed by some Sonar components
157        .unmask("org.slf4j.Logger")
158        .unmask("org.slf4j.ILoggerFactory")
159        // Exclude other slf4j classes
160        // .unmask("org.slf4j.impl.")
161        .mask("org.slf4j.")
162        // Exclude logback
163        .mask("ch.qos.logback.")
164        .mask("org.sonar.")
165        // Include everything else
166        .unmask("");
167    runner.addExtensions(session, getLog(), lifecycleExecutor, artifactFactory, localRepository, artifactMetadataSource, artifactCollector,
168        dependencyTreeBuilder, projectBuilder);
169    if (getLog().isDebugEnabled()) {
170      runner.setProperty("sonar.verbose", "true");
171    }
172    runner.execute();
173  }
174
175  private ArtifactVersion getMavenVersion() {
176    return runtimeInformation.getApplicationVersion();
177  }
178
179  public static String toString(Object obj) {
180    return obj == null ? "" : obj.toString();
181  }
182
183  public static String getSourceEncoding(MavenProject pom) {
184    return pom.getProperties().getProperty("project.build.sourceEncoding");
185  }
186
187  public static String getSonarKey(MavenProject pom) {
188    return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
189  }
190
191  public static File getSonarWorkDir(MavenProject pom) {
192    return new File(getBuildDir(pom), "sonar");
193  }
194
195  private static File getBuildDir(MavenProject pom) {
196    return resolvePath(pom.getBuild().getDirectory(), pom.getBasedir());
197  }
198
199  static File resolvePath(String path, File basedir) {
200    if (path != null) {
201      File file = new File(path);
202      if (!file.isAbsolute()) {
203        try {
204          file = new File(basedir, path).getCanonicalFile();
205        } catch (IOException e) {
206          throw new IllegalStateException("Unable to resolve path '" + path + "'", e);
207        }
208      }
209      return file;
210    }
211    return null;
212  }
213}