Java tutorial
/* * Copyright 2013 Andrew Everitt, Andrew Heckford, Daniele Masato * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.org.raje.maven.plugin.msbuild; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.codehaus.plexus.util.cli.StreamConsumer; import org.codehaus.plexus.util.cli.WriterStreamConsumer; import uk.org.raje.maven.plugin.msbuild.configuration.BuildConfiguration; import uk.org.raje.maven.plugin.msbuild.configuration.BuildPlatform; import uk.org.raje.maven.plugin.msbuild.configuration.CppCheckConfiguration; import uk.org.raje.maven.plugin.msbuild.parser.VCProject; import uk.org.raje.maven.plugin.msbuild.streamconsumers.StdoutStreamToLog; /** * Configure and run Cppcheck for static code analysis. */ @Mojo(name = CppCheckMojo.MOJO_NAME, defaultPhase = LifecyclePhase.VERIFY) public class CppCheckMojo extends AbstractMSBuildPluginMojo { /** * The name this Mojo declares, also represents the goal. */ public static final String MOJO_NAME = "cppcheck"; /** * The name of the directory created under 'target' where we store CppCheck report files. */ public static final String REPORT_DIRECTORY = "cppcheck-reports"; @Override public void doExecute() throws MojoExecutionException, MojoFailureException { boolean wasExecutionSuccessful = true; if (!isCppCheckEnabled(false)) { return; } validateCppCheckConfiguration(); for (BuildPlatform platform : platforms) { for (BuildConfiguration configuration : platform.getConfigurations()) { for (VCProject vcProject : getParsedProjects(platform, configuration, cppCheck.getExcludeProjectRegex())) { getLog().info("Running static code analysis for project " + vcProject.getName() + ", platform=" + vcProject.getPlatform() + ", configuration=" + vcProject.getConfiguration()); try { wasExecutionSuccessful &= runCppCheck(vcProject); } catch (MojoExecutionException mee) { getLog().error(mee.getMessage()); throw mee; } } } } if (!wasExecutionSuccessful) { throw new MojoFailureException("Static code analysis failed"); } getLog().info("Static code analysis complete"); } private void validateCppCheckConfiguration() throws MojoExecutionException, MojoFailureException { try { MojoHelper.validateToolPath(cppCheck.getCppCheckPath(), CppCheckConfiguration.TOOL_NAME, getLog()); } catch (FileNotFoundException fnfe) { throw new MojoExecutionException( CppCheckConfiguration.TOOL_NAME + "could not be found at " + fnfe.getMessage() + ". " + "You need to configure it in the plugin configuration section of the " + "POM file using <cppCheckPath>...</cppCheckPath> or " + "or <properties><" + CppCheckConfiguration.PATH_PROPERTY + ">...</" + CppCheckConfiguration.PATH_PROPERTY + "></properties>; " + "alternatively, you can use the command-line parameter -Dcppcheck.path=... " + "or set the environment variable " + CppCheckConfiguration.PATH_ENVVAR, fnfe); } validateProjectFile(); platforms = MojoHelper.validatePlatforms(platforms); } private String getSourcesForStdin(VCProject vcProject) throws MojoExecutionException { StringBuilder stringBuilder = new StringBuilder(); for (File sourceFile : getProjectSources(vcProject, false, cppCheck.getExcludes())) { try { stringBuilder.append(getRelativeFile(vcProject.getBaseDirectory(), sourceFile) + "\n"); } catch (IOException ioe) { throw new MojoExecutionException("Failed to compute relative path for file " + sourceFile, ioe); } } return stringBuilder.toString(); } private Writer createCppCheckReportWriter(File reportFile) throws MojoExecutionException { BufferedWriter cppCheckReportWriter; try { FileUtils.forceMkdir(reportFile.getParentFile()); cppCheckReportWriter = new BufferedWriter(new FileWriter(reportFile)); } catch (IOException ioe) { throw new MojoExecutionException( "Failed to create " + CppCheckConfiguration.TOOL_NAME + " report " + reportFile, ioe); } return cppCheckReportWriter; } private CppCheckRunner createCppCheckRunner(VCProject vcProject, StreamConsumer streamConsumer) throws MojoExecutionException { CppCheckRunner cppCheckRunner = new CppCheckRunner(cppCheck.getCppCheckPath(), streamConsumer, getLog()); cppCheckRunner.setWorkingDirectory(vcProject.getBaseDirectory()); cppCheckRunner.setStandardInputString(getSourcesForStdin(vcProject)); cppCheckRunner.setCppCheckType(cppCheck.getCppCheckType()); cppCheckRunner.setIncludeDirectories(getRelativeIncludeDirectories(vcProject)); cppCheckRunner.setPreprocessorDefs(vcProject.getPreprocessorDefs()); return cppCheckRunner; } /** * Adjust the list of include paths to be relative to the projectFile directory */ private List<File> getRelativeIncludeDirectories(VCProject vcProject) throws MojoExecutionException { final List<File> relativeIncludeDirectories = new ArrayList<File>(); for (File includeDir : vcProject.getIncludeDirectories()) { if (includeDir.isAbsolute()) { relativeIncludeDirectories.add(includeDir); } else { try { File absoluteIncludeDir = new File(vcProject.getFile().getParentFile(), includeDir.getPath()); relativeIncludeDirectories.add( getRelativeFile(vcProject.getBaseDirectory(), absoluteIncludeDir.getCanonicalFile())); } catch (IOException ioe) { throw new MojoExecutionException("Failed to compute relative path for directroy " + includeDir, ioe); } } } return relativeIncludeDirectories; } private boolean executeCppCheckRunner(CommandLineRunner cppCheckRunner) throws MojoExecutionException { try { return cppCheckRunner.runCommandLine() == 0; } catch (IOException ioe) { throw new MojoExecutionException("I/O error while executing command line ", ioe); } catch (InterruptedException ie) { throw new MojoExecutionException("Process interrupted while executing command line ", ie); } } private void finaliseReportWriter(Writer reportWriter, File reportFile) throws MojoExecutionException { try { reportWriter.close(); } catch (IOException ioe) { throw new MojoExecutionException( "Failed to finalise " + CppCheckConfiguration.TOOL_NAME + " report" + reportFile, ioe); } } private boolean runCppCheck(VCProject vcProject) throws MojoExecutionException, MojoFailureException { File reportFile = getReportFile(vcProject); Writer reportWriter = createCppCheckReportWriter(reportFile); CppCheckWriterStreamConsumer reportStreamConsumer = new CppCheckWriterStreamConsumer(reportWriter); CommandLineRunner cppCheckRunner = createCppCheckRunner(vcProject, reportStreamConsumer); boolean wasExecutionSuccessful = executeCppCheckRunner(cppCheckRunner); finaliseReportWriter(reportWriter, reportFile); if (reportStreamConsumer.isCheckConfigSuggested()) { CppCheckRunner cppCheckCheckConfigRunner = createCppCheckRunner(vcProject, new StdoutStreamToLog(getLog())); cppCheckCheckConfigRunner.setCheckConfig(true); executeCppCheckRunner(cppCheckCheckConfigRunner); } return wasExecutionSuccessful; } private File getReportFile(VCProject vcProject) { final File reportDirectory = new File(vcProject.getFile().getParentFile(), REPORT_DIRECTORY); return new File(reportDirectory, cppCheck.getReportName() + "-" + vcProject + ".xml"); } private static class CppCheckRunner extends CommandLineRunner { /** * Construct the CppCheckRunner * @param cppCheckPath the path to CppCheck.exe * @param reportConsumer a StreamConsumer to write the report to * @param log the Maven Log to use */ public CppCheckRunner(File cppCheckPath, StreamConsumer reportConsumer, Log log) { super(CppCheckConfiguration.TOOL_NAME, new StdoutStreamToLog(log), reportConsumer); this.cppCheckPath = cppCheckPath; } public void setCppCheckType(CppCheckConfiguration.CppCheckType cppCheckType) { this.cppCheckType = cppCheckType; } public void setIncludeDirectories(List<File> includeDirectories) { this.includeDirectories = includeDirectories; } public void setPreprocessorDefs(List<String> preprocessorDefs) { this.preprocessorDefs = preprocessorDefs; } public void setCheckConfig(boolean checkConfig) { this.checkConfig = checkConfig; } @Override protected List<String> getCommandLineArguments() { final String reportXMLVersion = "1"; final List<String> commandLineArguments = new LinkedList<String>(); commandLineArguments.add(cppCheckPath.getAbsolutePath()); commandLineArguments.add("--enable=" + cppCheckType.name()); for (File includeDirectory : includeDirectories) { //WARNING: remove any trailing slashes from include paths because CppCheck may fail if these are // present; using {@link File}s to wrap include paths is safe, whereas using {@link String}s may // cause problems commandLineArguments.add("-I"); commandLineArguments.add("\"" + includeDirectory + "\""); } for (String preprocessorDef : preprocessorDefs) { commandLineArguments.add("-D" + preprocessorDef); } commandLineArguments.add("--file-list=-"); if (checkConfig) { commandLineArguments.add("--check-config"); } else { commandLineArguments.add("--xml"); commandLineArguments.add("--xml-version=" + reportXMLVersion); } commandLineArguments.add("--quiet"); return commandLineArguments; } private File cppCheckPath; private CppCheckConfiguration.CppCheckType cppCheckType = CppCheckConfiguration.CppCheckType.all; private List<File> includeDirectories; private List<String> preprocessorDefs; private boolean checkConfig = false; } /** * Override WriterStreamConsumer to add a check for message suggesting running with '--check-config' */ private static class CppCheckWriterStreamConsumer extends WriterStreamConsumer { CppCheckWriterStreamConsumer(Writer writer) { super(writer); } @Override public void consumeLine(String line) { if (line.contains("--check-config")) { checkConfigSuggested = true; } super.consumeLine(line); } boolean isCheckConfigSuggested() { return checkConfigSuggested; } private boolean checkConfigSuggested = false; } }