/* * Copyright 2005-2016 Sixth and Red River Software, Bas Leijdekkers * * 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 com.sixrr.metrics.offline; import com.intellij.analysis.AnalysisScope; import com.intellij.ide.impl.PatchProjectUtil; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.ApplicationStarter; import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; import com.intellij.psi.search.GlobalSearchScopesCore; import com.intellij.psi.search.scope.packageSet.NamedScope; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; import com.sixrr.metrics.export.Exporter; import com.sixrr.metrics.export.XMLExporter; import com.sixrr.metrics.metricModel.MetricsExecutionContextImpl; import com.sixrr.metrics.metricModel.MetricsRunImpl; import com.sixrr.metrics.metricModel.TimeStamp; import com.sixrr.metrics.profile.MetricsProfile; import com.sixrr.metrics.profile.MetricsProfileRepository; import org.jetbrains.annotations.Contract; import org.kohsuke.args4j.*; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; public class MetricsCommandLine implements ApplicationStarter { private static final Logger LOG = Logger.getInstance("MetricsReloaded"); @Argument(index = 0, required = true, metaVar = "<project_path>", usage = "the project to calculate metrics for") private String projectPath = null; @Argument(index = 1, required = true, metaVar = "<metrics_profile_name>", usage = "name of the metrics profile to use") private String metricsProfileName = ""; @Argument(index = 2, metaVar = "<output_path>", usage = "the path to write the output xml to, default writes to STDOUT") private String outputXmlPath = null; @Option(name = "-d", aliases = "--directory", metaVar = "<path>", forbids = "-s", usage = "directory to calculate metrics for, default is the whole project") private String directory = null; @Option(name = "-s", aliases = "--scope", metaVar = "<scope_name>", forbids = "-d", usage = "name of scope to calculate metrics for, default is the whole project") private String scope = null; @Option(name = "-v", aliases = "--verbose", usage = "show more progress information", forbids = "-q") private boolean verbose = false; @Option(name = "-q", aliases = "--quiet", usage = "show less information", forbids = "-v") private boolean quiet = false; @Option(name = "-h", aliases = "--help", usage = "show this message", help = true) private boolean help = false; @Override public String getCommandName() { return "metrics"; } private static void printUsage(CmdLineParser parser, PrintStream out) { final String scriptName = ApplicationNamesInfo.getInstance().getScriptName(); out.println("Usage: " + scriptName + " metrics [options] <project_path> <metrics_profile_name> [<output_xml_file>]"); parser.printUsage(out); } @Override public void premain(String[] args) { final ParserProperties properties = ParserProperties.defaults() .withShowDefaults(false) .withOptionSorter(null); final CmdLineParser parser = new CmdLineParser(this, properties); try { parser.parseArgument(Arrays.copyOfRange(args, 1, args.length)); if (help) { printUsage(parser, System.out); System.exit(0); } } catch (CmdLineException e) { System.err.println(e.getMessage()); printUsage(parser, System.err); System.exit(1); } } @Override public void main(String[] args) { if (outputXmlPath != null) { final File file = new File(outputXmlPath); final File parentFile = file.getParentFile(); if (parentFile != null && !parentFile.exists()) { error("Could not find directory " + parentFile.getAbsolutePath()); } } final ApplicationEx application = (ApplicationEx) ApplicationManager.getApplication(); try { final ApplicationInfoEx applicationInfo = (ApplicationInfoEx) ApplicationInfo.getInstance(); info("MetricsReloaded running on " + applicationInfo.getFullApplicationName()); application.doNotSave(); try { info("Opening project..."); if (projectPath == null) { projectPath = new File("").getAbsolutePath(); } projectPath = projectPath.replace(File.separatorChar, '/'); final Project project = ProjectUtil.openOrImport(projectPath, null, false); if (project == null) { error("Unable to open project: " + projectPath); } application.runWriteAction(new Runnable() { @Override public void run() { VirtualFileManager.getInstance().refreshWithoutFileWatcher(false); } }); PatchProjectUtil.patchProject(project); info("Project " + project.getName() + " opened."); final MetricsProfile profile = getMetricsProfile(metricsProfileName); if (profile == null) { error("Profile not found: " + metricsProfileName); } info("Calculating metrics"); final AnalysisScope analysisScope; if (scope != null) { final NamedScope namedScope = NamedScopesHolder.getScope(project, scope); if (namedScope == null) { error("Scope not found: " + scope); } analysisScope = new AnalysisScope(GlobalSearchScopesCore.filterScope(project, namedScope), project); } else if (directory != null) { directory = directory.replace(File.separatorChar, '/'); final VirtualFile vfsDir = LocalFileSystem.getInstance().findFileByPath(directory); if (vfsDir == null) { error("Directory not found: " + directory); } final PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(vfsDir); if (psiDirectory == null) { error("Directory not found: " + directory); } analysisScope = new AnalysisScope(psiDirectory); } else { analysisScope = new AnalysisScope(project); } ProgressManager.getInstance().runProcess(new Runnable() { @Override public void run() { final MetricsRunImpl metricsRun = new MetricsRunImpl(); metricsRun.setProfileName(profile.getName()); metricsRun.setTimestamp(new TimeStamp()); metricsRun.setContext(analysisScope); final MetricsExecutionContextImpl metricsExecutionContext = new MetricsExecutionContextImpl(project, analysisScope); metricsExecutionContext.calculateMetrics(profile, metricsRun); final Exporter exporter = new XMLExporter(metricsRun); try { if (outputXmlPath == null) { final PrintWriter writer = new PrintWriter(System.out, true); exporter.export(writer); } else { exporter.export(outputXmlPath); } } catch (IOException e) { error(e.getMessage()); } } }, new ProgressIndicatorBase() { private int lastPercent = 0; @Override public void setFraction(double fraction) { final int percent = (int)(fraction * 100); if (lastPercent != percent && !isIndeterminate()) { lastPercent = percent; trace("Calculating metrics " + lastPercent + "%"); } } }); info("Finished."); } catch (Exception ex) { error(ex); } application.exit(true, true); } catch (Exception e) { LOG.error(e); error(e); } } private static MetricsProfile getMetricsProfile(String profileName) { final MetricsProfileRepository repository = MetricsProfileRepository.getInstance(); final List<String> metricsProfileNames = Arrays.asList(repository.getProfileNames()); if (!metricsProfileNames.contains(profileName)) { return null; } repository.setSelectedProfile(profileName); return repository.getCurrentProfile(); } @Contract("_ -> fail") private static void error(Throwable throwable) { System.err.println(throwable.getMessage()); LOG.error(throwable); System.exit(1); } @Contract("_ -> fail") private static void error(String message) { System.err.println(message); System.exit(1); } private void info(String message) { if (quiet) { return; } System.out.println(message); } private void trace(String message) { if (!verbose) { return; } System.out.println(message); } }