QD-8426: Launch Qodana using ModernApplicationStarter

[QD-8426] Properly suspend in project opening & configuration

Like in db06df3bd4ce31fbd56d74dccda89b15322b4136 we need to use different
dispatching behaviour in prod/test to avoid an EDT deadlock, this time in
the golang configurator

[QD-8426] Rename interface

[QD-8426] Merge InspectionApplication into InspectionApplicationBase

Add deprecation annotation because of API check

[QD-8426] Delegate all message reporting

[QD-8429] Remove ProgressManager from QodanaInspectionApplication

[QD-8426] Fully decouple QodanaInspectionApplication from InspectionApplicationBase

[QD-8426] Update InspectionMain to ModernApplicationStarter

[QD-8426] Convert InspectionApplicationFactory and InspectionMain to kotlin

Rename .java to .kt

QD-8426


Merge-request: IJ-MR-126583
Merged-by: Johannes Koenen <Johannes.Koenen@jetbrains.com>

GitOrigin-RevId: bc152d816e2acad9bd20a6c7db512108377995b0
This commit is contained in:
Johannes Koenen
2024-02-26 17:45:11 +00:00
committed by intellij-monorepo-bot
parent 7ab8560477
commit e2944e826f
9 changed files with 178 additions and 168 deletions

View File

@@ -45,7 +45,7 @@ public abstract class AbstractInspectionCmdlineOptions implements InspectionTool
protected abstract String @NotNull [] optionsBanner();
@Override
public void initApplication(InspectionApplication app) {
public void initApplication(InspectionApplicationBase app) {
app.myHelpProvider = this;
app.myProjectPath = determineProjectPath();
app.myProfileName = getProfileNameOrPathProperty();

View File

@@ -13,7 +13,7 @@ import java.util.concurrent.ForkJoinPool;
@SuppressWarnings("UseOfSystemOutOrSystemErr")
public abstract class AbstractInspectionToolStarter implements ApplicationStarter {
private InspectionApplication myApplication;
private InspectionApplicationBase myApplication;
protected InspectionToolCmdlineOptions myOptions;
protected abstract AbstractInspectionCmdlineOptions createCmdlineOptions();
@@ -56,13 +56,13 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
System.exit(1);
}
myApplication = new InspectionApplication();
myApplication = new InspectionApplicationBase();
initApplication(myApplication, myOptions);
// TODO: keep application settings in Memory
}
protected InspectionApplication getApplication() {
protected InspectionApplicationBase getApplication() {
return myApplication;
}
@@ -71,7 +71,7 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
myOptions.beforeStartup();
IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(true);
InspectionApplication.LOG.info(
InspectionApplicationBase.LOG.info(
"CPU cores: " + Runtime.getRuntime().availableProcessors() + "; " +
"ForkJoinPool.commonPool: " + ForkJoinPool.commonPool() + "; " +
"factory: " + ForkJoinPool.commonPool().getFactory());
@@ -79,7 +79,7 @@ public abstract class AbstractInspectionToolStarter implements ApplicationStarte
myApplication.startup();
}
private static void initApplication(@NotNull InspectionApplication application,
private static void initApplication(@NotNull InspectionApplicationBase application,
@NotNull InspectionToolCmdlineOptions opts) {
opts.initApplication(application);
}

View File

@@ -1,8 +1,10 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.openapi.diagnostic.Logger;
/**
* @deprecated Inherit directly from InspectionApplicationBase
*/
@Deprecated(forRemoval = true)
public final class InspectionApplication extends InspectionApplicationBase {
static final Logger LOG = Logger.getInstance(InspectionApplication.class);
}

View File

@@ -62,7 +62,6 @@ import one.util.streamex.StreamEx;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import org.jetbrains.concurrency.AsyncPromise;
import javax.xml.stream.XMLStreamException;
@@ -79,8 +78,8 @@ import java.util.function.Predicate;
import static com.intellij.configurationStore.StoreUtilKt.forPoorJavaClientOnlySaveProjectIndEdtDoNotUseThisMethod;
public class InspectionApplicationBase implements CommandLineInspectionProgressReporter {
private static final Logger LOG = Logger.getInstance(InspectionApplicationBase.class);
public class InspectionApplicationBase implements CommandLineInspectionProgressReporter, InspectionApplicationStart.Synchronous {
public static final Logger LOG = Logger.getInstance(InspectionApplicationBase.class);
public static final String PROJECT_STRUCTURE_DIR = "projectStructure";
@@ -272,8 +271,7 @@ public class InspectionApplicationBase implements CommandLineInspectionProgressR
return project;
}
@VisibleForTesting
public @Nullable AnalysisScope getAnalysisScope(@NotNull Project project) throws ExecutionException, InterruptedException {
private @Nullable AnalysisScope getAnalysisScope(@NotNull Project project) throws ExecutionException, InterruptedException {
SearchScope scope = getSearchScope(project);
if (scope == null) return null;
return new AnalysisScope(scope, project);

View File

@@ -1,28 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.openapi.extensions.ExtensionPointName;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@ApiStatus.Internal
public interface InspectionApplicationFactory {
ExtensionPointName<InspectionApplicationFactory> EP_NAME = ExtensionPointName.create("com.intellij.inspectionApplicationFactory");
@NotNull
String id();
InspectionApplicationBase getApplication(@NotNull List<String> args) throws InspectionApplicationException;
@NotNull
static InspectionApplicationBase getApplication(@NotNull String id, @NotNull List<String> args) throws InspectionApplicationException {
for (InspectionApplicationFactory extension : EP_NAME.getExtensionList()) {
if (extension.id().equals(id)) {
return extension.getApplication(args);
}
}
throw new InspectionApplicationException("There is no loaded inspect engine with id= '" + id + "'. Please check loaded plugin list.");
}
}

View File

@@ -0,0 +1,34 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.ExtensionPointName.Companion.create
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface InspectionApplicationFactory {
fun id(): String
fun getApplication(args: List<String>): InspectionApplicationStart
companion object {
@JvmStatic
fun getApplication(id: String, args: List<String>): InspectionApplicationStart =
EP_NAME.extensionList
.firstOrNull { it.id() == id }
?.getApplication(args)
?: throw InspectionApplicationException("There is no loaded inspect engine with id= '$id'. Please check loaded plugin list.")
val EP_NAME: ExtensionPointName<InspectionApplicationFactory> = create("com.intellij.inspectionApplicationFactory")
}
}
sealed interface InspectionApplicationStart {
interface Synchronous : InspectionApplicationStart {
fun startup()
}
interface Asynchronous : InspectionApplicationStart {
suspend fun startup()
}
}

View File

@@ -1,126 +0,0 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.openapi.application.ApplicationStarter;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@SuppressWarnings({"CallToPrintStackTrace", "UseOfSystemOutOrSystemErr"})
public class InspectionMain implements ApplicationStarter {
private InspectionApplicationBase myApplication;
@Override
public String getCommandName() {
return "inspect";
}
@Override
public int getRequiredModality() {
return NOT_IN_EDT;
}
@Override
public void premain(@NotNull List<String> args) {
InspectionApplication.LOG.info("Command line arguments: " + args);
if (args.size() > 1 && "qodana".equals(args.get(1))) {
try {
myApplication = InspectionApplicationFactory.getApplication("qodana", args.subList(2, args.size()));
}
catch (InspectionApplicationException e) {
System.err.println(e.getMessage());
System.exit(1);
}
catch (Exception e) {
e.printStackTrace(); // workaround for IDEA-289086
System.exit(1);
}
return;
}
myApplication = new InspectionApplication();
if (args.size() < 4) {
System.err.println("invalid args:" + args);
printHelpAndExit();
}
myApplication.myHelpProvider = InspectionMain::printHelpAndExit;
myApplication.myProjectPath = args.get(1);
myApplication.myStubProfile = args.get(2);
myApplication.myOutPath = args.get(3);
if (myApplication.myProjectPath == null
|| myApplication.myOutPath == null
|| myApplication.myStubProfile == null) {
System.err.println(myApplication.myProjectPath + myApplication.myOutPath + myApplication.myStubProfile);
printHelpAndExit();
}
try {
for (int i = 4; i < args.size(); i++) {
String arg = args.get(i);
if ("-profileName".equals(arg)) {
myApplication.myProfileName = args.get(++i);
}
else if ("-profilePath".equals(arg)) {
myApplication.myProfilePath = args.get(++i);
}
else if ("-d".equals(arg)) {
myApplication.mySourceDirectory = args.get(++i);
}
else if ("-scope".equals(arg)) {
myApplication.myScopePattern = args.get(++i);
}
else if ("-targets".equals(arg)) {
myApplication.myTargets = args.get(++i);
}
else if ("-format".equals(arg)) {
myApplication.myOutputFormat = args.get(++i);
}
else if ("-v0".equals(arg)) {
myApplication.setVerboseLevel(0);
}
else if ("-v1".equals(arg)) {
myApplication.setVerboseLevel(1);
}
else if ("-v2".equals(arg)) {
myApplication.setVerboseLevel(2);
}
else if ("-v3".equals(arg)) {
myApplication.setVerboseLevel(3);
}
else if ("-e".equals(arg)) {
myApplication.myRunWithEditorSettings = true;
}
else if ("-t".equals(arg)) {
myApplication.myErrorCodeRequired = false;
}
else if ("-changes".equals(arg)) {
myApplication.myAnalyzeChanges = true;
}
else //noinspection StatementWithEmptyBody
if ("-qodana".equals(arg)) {
}
else {
System.err.println("unexpected argument: " + arg);
printHelpAndExit();
}
}
}
catch (IndexOutOfBoundsException e) {
e.printStackTrace();
printHelpAndExit();
}
myApplication.myRunGlobalToolsOnly = System.getProperty("idea.no.local.inspections") != null;
}
@Override
public void main(@NotNull List<String> args) {
myApplication.startup();
}
private static void printHelpAndExit() {
System.out.println(InspectionsBundle.message("inspection.command.line.explanation"));
System.exit(1);
}
}

View File

@@ -0,0 +1,130 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection
import com.intellij.codeInspection.InspectionApplicationFactory.Companion.getApplication
import com.intellij.openapi.application.ModernApplicationStarter
import java.util.concurrent.CompletableFuture
import kotlin.system.exitProcess
class InspectionMain : ModernApplicationStarter() {
private lateinit var application: InspectionApplicationStart
override fun premain(args: List<String>) {
InspectionApplicationBase.LOG.info("Command line arguments: $args")
application = if (args.size > 1 && "qodana" == args[1]) {
buildQodanaApplication(args)
}
else {
buildInspectionApplication(args)
}
}
private fun buildQodanaApplication(args: List<String>) =
try {
getApplication("qodana", args.subList(2, args.size))
}
catch (e: InspectionApplicationException) {
System.err.println(e.message)
exitProcess(1)
}
catch (e: Exception) {
e.printStackTrace() // workaround for IDEA-289086
exitProcess(1)
}
private fun buildInspectionApplication(args: List<String>): InspectionApplicationStart {
val app = InspectionApplicationBase()
if (args.size < 4) {
System.err.println("invalid args:$args")
printHelpAndExit()
}
app.myHelpProvider = InspectionToolCmdlineOptionHelpProvider { printHelpAndExit() }
app.myProjectPath = args[1]
app.myStubProfile = args[2]
app.myOutPath = args[3]
if (app.myProjectPath == null || app.myOutPath == null || app.myStubProfile == null) {
System.err.println(app.myProjectPath + app.myOutPath + app.myStubProfile)
printHelpAndExit()
}
try {
var i = 4
while (i < args.size) {
when (val arg = args[i]) {
"-profileName" -> {
app.myProfileName = args[++i]
}
"-profilePath" -> {
app.myProfilePath = args[++i]
}
"-d" -> {
app.mySourceDirectory = args[++i]
}
"-scope" -> {
app.myScopePattern = args[++i]
}
"-targets" -> {
app.myTargets = args[++i]
}
"-format" -> {
app.myOutputFormat = args[++i]
}
"-v0" -> {
app.setVerboseLevel(0)
}
"-v1" -> {
app.setVerboseLevel(1)
}
"-v2" -> {
app.setVerboseLevel(2)
}
"-v3" -> {
app.setVerboseLevel(3)
}
"-e" -> {
app.myRunWithEditorSettings = true
}
"-t" -> {
app.myErrorCodeRequired = false
}
"-changes" -> {
app.myAnalyzeChanges = true
}
"-qodana" -> { /* do nothing */
}
else -> {
System.err.println("unexpected argument: $arg")
printHelpAndExit()
}
}
i++
}
}
catch (e: IndexOutOfBoundsException) {
e.printStackTrace()
printHelpAndExit()
}
app.myRunGlobalToolsOnly = System.getProperty("idea.no.local.inspections") != null
return app
}
override suspend fun start(args: List<String>) {
when (val r = application) {
is InspectionApplicationStart.Asynchronous -> r.startup()
/*
todo https://youtrack.jetbrains.com/issue/IDEA-298594
See also com.intellij.platform.ide.bootstrap.ApplicationLoader.executeApplicationStarter
*/
is InspectionApplicationStart.Synchronous -> CompletableFuture.runAsync(r::startup)
}
}
private fun printHelpAndExit() {
println(InspectionsBundle.message("inspection.command.line.explanation"))
exitProcess(1)
}
}

View File

@@ -7,7 +7,7 @@ public interface InspectionToolCmdlineOptions extends InspectionToolCmdlineOptio
/**
* @param app Inspection Application
*/
void initApplication(InspectionApplication app);
void initApplication(InspectionApplicationBase app);
/**
* @return 0 if turned off