diff --git a/.bazelrc b/.bazelrc index 0ce41ea4af9..4acbd8f785f 100644 --- a/.bazelrc +++ b/.bazelrc @@ -3,8 +3,8 @@ build --tool_java_language_version=21 --tool_java_runtime_version=21 # Delete test data packages, needed for bazel integration tests. Update by running the following command: # bazel run @rules_bazel_integration_test//tools:update_deleted_packages -build --deleted_packages=aspect/testing/tests/src/com/google/idea/blaze/aspect/integration/testdata,clwb/tests/projects/clang_cl,clwb/tests/projects/clang_cl/main,clwb/tests/projects/external_includes/main,clwb/tests/projects/llvm_toolchain/main,clwb/tests/projects/llvm_toolchain/wasm,clwb/tests/projects/query_sync/main,clwb/tests/projects/simple/main,clwb/tests/projects/target_compatible/main,clwb/tests/projects/virtual_includes/lib/impl_deps,clwb/tests/projects/virtual_includes/lib/raw_files,clwb/tests/projects/virtual_includes/lib/strip_absolut,clwb/tests/projects/virtual_includes/lib/strip_relative,clwb/tests/projects/virtual_includes/main,examples/cpp/simple_project/.clwb/aspects,examples/cpp/simple_project/src,examples/cpp/simple_project/src/lib,examples/go/with_go_source,examples/go/with_go_source/otherlib,examples/go/with_go_source/testa,examples/go/with_go_source/testb,examples/go/with_proto,examples/go/with_proto/go,examples/go/with_proto/go/external,examples/go/with_proto/go/lib,examples/go/with_proto/proto,examples/java/greetings_project,examples/java/greetings_project/greeting_lib,examples/java/greetings_project/hello,examples/java/greetings_project/hi,examples/kotlin/simple_project,examples/python/simple_code_generator/example,examples/python/simple_code_generator/generated,examples/python/simple_code_generator/lib,examples/python/simple_code_generator/rules,examples/python/with_numpy,examples/python/with_numpy/app,examples/python/with_numpy/lib,examples/scala/with_bzlmod/hello,ijwb/tests/projects/simple,testing/test_deps/projects,testing/test_deps/projects/java_and_deps,testing/test_deps/projects/java_and_deps/deps/no_ide,testing/test_deps/projects/java_and_deps/deps/top_level_lib_1,testing/test_deps/projects/java_and_deps/deps/top_level_lib_2,testing/test_deps/projects/java_and_deps/deps/transitive_dep_lib,testing/test_deps/projects/java_and_deps/project,testing/test_deps/projects/java_and_deps/project/java/com/example/sample/nested,testing/test_deps/projects/simple_java,testing/test_deps/projects/simple_java/java/com/example/sample/nested,testing/test_deps/projects/simple_proto/external,testing/test_deps/projects/simple_proto/project -query --deleted_packages=aspect/testing/tests/src/com/google/idea/blaze/aspect/integration/testdata,clwb/tests/projects/clang_cl,clwb/tests/projects/clang_cl/main,clwb/tests/projects/external_includes/main,clwb/tests/projects/llvm_toolchain/main,clwb/tests/projects/llvm_toolchain/wasm,clwb/tests/projects/query_sync/main,clwb/tests/projects/simple/main,clwb/tests/projects/target_compatible/main,clwb/tests/projects/virtual_includes/lib/impl_deps,clwb/tests/projects/virtual_includes/lib/raw_files,clwb/tests/projects/virtual_includes/lib/strip_absolut,clwb/tests/projects/virtual_includes/lib/strip_relative,clwb/tests/projects/virtual_includes/main,examples/cpp/simple_project/.clwb/aspects,examples/cpp/simple_project/src,examples/cpp/simple_project/src/lib,examples/go/with_go_source,examples/go/with_go_source/otherlib,examples/go/with_go_source/testa,examples/go/with_go_source/testb,examples/go/with_proto,examples/go/with_proto/go,examples/go/with_proto/go/external,examples/go/with_proto/go/lib,examples/go/with_proto/proto,examples/java/greetings_project,examples/java/greetings_project/greeting_lib,examples/java/greetings_project/hello,examples/java/greetings_project/hi,examples/kotlin/simple_project,examples/python/simple_code_generator/example,examples/python/simple_code_generator/generated,examples/python/simple_code_generator/lib,examples/python/simple_code_generator/rules,examples/python/with_numpy,examples/python/with_numpy/app,examples/python/with_numpy/lib,examples/scala/with_bzlmod/hello,ijwb/tests/projects/simple,testing/test_deps/projects,testing/test_deps/projects/java_and_deps,testing/test_deps/projects/java_and_deps/deps/no_ide,testing/test_deps/projects/java_and_deps/deps/top_level_lib_1,testing/test_deps/projects/java_and_deps/deps/top_level_lib_2,testing/test_deps/projects/java_and_deps/deps/transitive_dep_lib,testing/test_deps/projects/java_and_deps/project,testing/test_deps/projects/java_and_deps/project/java/com/example/sample/nested,testing/test_deps/projects/simple_java,testing/test_deps/projects/simple_java/java/com/example/sample/nested,testing/test_deps/projects/simple_proto/external,testing/test_deps/projects/simple_proto/project +build --deleted_packages=.bazelbsp,.bazelbsp/aspects,aspect/testing/tests/src/com/google/idea/blaze/aspect/integration/testdata,clwb/tests/projects/clang_cl,clwb/tests/projects/clang_cl/main,clwb/tests/projects/execution/main,clwb/tests/projects/external_includes/main,clwb/tests/projects/llvm_toolchain/main,clwb/tests/projects/llvm_toolchain/wasm,clwb/tests/projects/query_sync/main,clwb/tests/projects/simple/main,clwb/tests/projects/target_compatible/main,clwb/tests/projects/virtual_includes/.clwb/aspects,clwb/tests/projects/virtual_includes/lib/impl_deps,clwb/tests/projects/virtual_includes/lib/raw_files,clwb/tests/projects/virtual_includes/lib/strip_absolut,clwb/tests/projects/virtual_includes/lib/strip_relative,clwb/tests/projects/virtual_includes/main,examples/cpp/simple_project/.clwb/aspects,examples/cpp/simple_project/src,examples/cpp/simple_project/src/lib,examples/go/with_go_source,examples/go/with_go_source/otherlib,examples/go/with_go_source/testa,examples/go/with_go_source/testb,examples/go/with_proto,examples/go/with_proto/go,examples/go/with_proto/go/external,examples/go/with_proto/go/lib,examples/go/with_proto/proto,examples/java/greetings_project,examples/java/greetings_project/.ijwb/aspects,examples/java/greetings_project/greeting_lib,examples/java/greetings_project/hello,examples/java/greetings_project/hi,examples/kotlin/simple_project,examples/python/simple_code_generator/example,examples/python/simple_code_generator/generated,examples/python/simple_code_generator/lib,examples/python/simple_code_generator/rules,examples/python/with_numpy,examples/python/with_numpy/app,examples/python/with_numpy/lib,examples/scala/with_bzlmod/hello,ijwb/tests/projects/simple,testing/test_deps/projects,testing/test_deps/projects/java_and_deps,testing/test_deps/projects/java_and_deps/deps/no_ide,testing/test_deps/projects/java_and_deps/deps/top_level_lib_1,testing/test_deps/projects/java_and_deps/deps/top_level_lib_2,testing/test_deps/projects/java_and_deps/deps/transitive_dep_lib,testing/test_deps/projects/java_and_deps/project,testing/test_deps/projects/java_and_deps/project/java/com/example/sample/nested,testing/test_deps/projects/simple_java,testing/test_deps/projects/simple_java/java/com/example/sample/nested,testing/test_deps/projects/simple_proto/external,testing/test_deps/projects/simple_proto/project +query --deleted_packages=.bazelbsp,.bazelbsp/aspects,aspect/testing/tests/src/com/google/idea/blaze/aspect/integration/testdata,clwb/tests/projects/clang_cl,clwb/tests/projects/clang_cl/main,clwb/tests/projects/execution/main,clwb/tests/projects/external_includes/main,clwb/tests/projects/llvm_toolchain/main,clwb/tests/projects/llvm_toolchain/wasm,clwb/tests/projects/query_sync/main,clwb/tests/projects/simple/main,clwb/tests/projects/target_compatible/main,clwb/tests/projects/virtual_includes/.clwb/aspects,clwb/tests/projects/virtual_includes/lib/impl_deps,clwb/tests/projects/virtual_includes/lib/raw_files,clwb/tests/projects/virtual_includes/lib/strip_absolut,clwb/tests/projects/virtual_includes/lib/strip_relative,clwb/tests/projects/virtual_includes/main,examples/cpp/simple_project/.clwb/aspects,examples/cpp/simple_project/src,examples/cpp/simple_project/src/lib,examples/go/with_go_source,examples/go/with_go_source/otherlib,examples/go/with_go_source/testa,examples/go/with_go_source/testb,examples/go/with_proto,examples/go/with_proto/go,examples/go/with_proto/go/external,examples/go/with_proto/go/lib,examples/go/with_proto/proto,examples/java/greetings_project,examples/java/greetings_project/.ijwb/aspects,examples/java/greetings_project/greeting_lib,examples/java/greetings_project/hello,examples/java/greetings_project/hi,examples/kotlin/simple_project,examples/python/simple_code_generator/example,examples/python/simple_code_generator/generated,examples/python/simple_code_generator/lib,examples/python/simple_code_generator/rules,examples/python/with_numpy,examples/python/with_numpy/app,examples/python/with_numpy/lib,examples/scala/with_bzlmod/hello,ijwb/tests/projects/simple,testing/test_deps/projects,testing/test_deps/projects/java_and_deps,testing/test_deps/projects/java_and_deps/deps/no_ide,testing/test_deps/projects/java_and_deps/deps/top_level_lib_1,testing/test_deps/projects/java_and_deps/deps/top_level_lib_2,testing/test_deps/projects/java_and_deps/deps/transitive_dep_lib,testing/test_deps/projects/java_and_deps/project,testing/test_deps/projects/java_and_deps/project/java/com/example/sample/nested,testing/test_deps/projects/simple_java,testing/test_deps/projects/simple_java/java/com/example/sample/nested,testing/test_deps/projects/simple_proto/external,testing/test_deps/projects/simple_proto/project common --enable_bzlmod common --noincompatible_disallow_empty_glob diff --git a/aspect/intellij_info_impl.bzl b/aspect/intellij_info_impl.bzl index f6d82a5c4eb..3657a22b23d 100644 --- a/aspect/intellij_info_impl.bzl +++ b/aspect/intellij_info_impl.bzl @@ -545,6 +545,7 @@ def collect_cpp_info(target, ctx, semantics, ide_info, ide_info_file, output_gro target_copts += semantics.cc.get_default_copts(ctx) target_copts = _do_starlark_string_expansion(ctx, "copt", target_copts, extra_targets) + args = _do_starlark_string_expansion(ctx, "args", getattr(ctx.rule.attr, "args", []), extra_targets) compilation_context = target[CcInfo].compilation_context @@ -571,6 +572,7 @@ def collect_cpp_info(target, ctx, semantics, ide_info, ide_info_file, output_gro transitive_system_include_directory = compilation_context.system_includes.to_list() + external_includes, include_prefix = getattr(ctx.rule.attr, "include_prefix", None), strip_include_prefix = getattr(ctx.rule.attr, "strip_include_prefix", None), + args = args, ) ide_info["c_ide_info"] = c_info resolve_files = compilation_context.headers diff --git a/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java b/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java index 6299e1274b3..3597928cf2c 100644 --- a/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java +++ b/base/src/com/google/idea/blaze/base/ideinfo/CIdeInfo.java @@ -38,6 +38,8 @@ public final class CIdeInfo implements ProtoWrapper { private final String includePrefix; private final String stripIncludePrefix; + private final ImmutableList args; + private CIdeInfo( ImmutableList sources, ImmutableList headers, @@ -48,7 +50,8 @@ private CIdeInfo( ImmutableList transitiveDefines, ImmutableList transitiveSystemIncludeDirectories, String includePrefix, - String stripIncludePrefix) { + String stripIncludePrefix, + ImmutableList args) { this.sources = sources; this.headers = headers; this.textualHeaders = textualHeaders; @@ -59,6 +62,7 @@ private CIdeInfo( this.transitiveSystemIncludeDirectories = transitiveSystemIncludeDirectories; this.includePrefix = includePrefix; this.stripIncludePrefix = stripIncludePrefix; + this.args = args; } static CIdeInfo fromProto(IntellijIdeInfo.CIdeInfo proto) { @@ -74,7 +78,8 @@ static CIdeInfo fromProto(IntellijIdeInfo.CIdeInfo proto) { ProtoWrapper.map( proto.getTransitiveSystemIncludeDirectoryList(), ExecutionRootPath::fromProto), proto.getIncludePrefix(), - proto.getStripIncludePrefix()); + proto.getStripIncludePrefix(), + ProtoWrapper.internStrings(proto.getArgsList())); } @Override @@ -90,6 +95,7 @@ public IntellijIdeInfo.CIdeInfo toProto() { .addAllTransitiveDefine(transitiveDefines) .addAllTransitiveSystemIncludeDirectory( ProtoWrapper.mapToProtos(transitiveSystemIncludeDirectories)) + .addAllArgs(args) .build(); } @@ -133,6 +139,10 @@ public String getStripIncludePrefix() { return stripIncludePrefix; } + public ImmutableList getArgs() { + return args; + } + public static Builder builder() { return new Builder(); } @@ -155,6 +165,8 @@ public static class Builder { private String includePrefix = ""; private String stripIncludePrefix = ""; + private final ImmutableList.Builder args = ImmutableList.builder(); + @CanIgnoreReturnValue public Builder addSources(Iterable sources) { this.sources.addAll(sources); @@ -247,7 +259,8 @@ public CIdeInfo build() { transitiveDefines.build(), transitiveSystemIncludeDirectories.build(), includePrefix, - stripIncludePrefix); + stripIncludePrefix, + args.build()); } } @@ -279,6 +292,9 @@ public String toString() { + " transitiveSystemIncludeDirectories=" + getTransitiveSystemIncludeDirectories() + "\n" + + " args=" + + getArgs() + + "\n" + '}'; } @@ -300,7 +316,8 @@ public boolean equals(Object o) { transitiveQuoteIncludeDirectories, cIdeInfo.transitiveQuoteIncludeDirectories) && Objects.equals(transitiveDefines, cIdeInfo.transitiveDefines) && Objects.equals( - transitiveSystemIncludeDirectories, cIdeInfo.transitiveSystemIncludeDirectories); + transitiveSystemIncludeDirectories, cIdeInfo.transitiveSystemIncludeDirectories) + && Objects.equals(args, cIdeInfo.args); } @Override @@ -313,6 +330,7 @@ public int hashCode() { transitiveIncludeDirectories, transitiveQuoteIncludeDirectories, transitiveDefines, - transitiveSystemIncludeDirectories); + transitiveSystemIncludeDirectories, + args); } } diff --git a/base/src/com/google/idea/blaze/base/ideinfo/TargetMap.java b/base/src/com/google/idea/blaze/base/ideinfo/TargetMap.java index f73c0a1a1cf..6464c5490c6 100644 --- a/base/src/com/google/idea/blaze/base/ideinfo/TargetMap.java +++ b/base/src/com/google/idea/blaze/base/ideinfo/TargetMap.java @@ -17,8 +17,11 @@ import com.google.common.base.Functions; import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.intellij.model.ProjectData; +import com.google.idea.blaze.base.model.primitives.Label; +import java.util.Map.Entry; import java.util.Objects; import javax.annotation.Nullable; @@ -50,6 +53,14 @@ public TargetIdeInfo get(TargetKey key) { return targetMap.get(key); } + public ImmutableList get(Label label) { + return targetMap.entrySet() + .stream() + .filter(it -> it.getKey().getLabel().equals(label)) + .map(Entry::getValue) + .collect(ImmutableList.toImmutableList()); + } + public boolean contains(TargetKey key) { return targetMap.containsKey(key); } diff --git a/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java b/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java index 9aae1727e98..f0627db0166 100644 --- a/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java +++ b/base/src/com/google/idea/blaze/base/run/BlazeBuildTargetRunConfigurationFactory.java @@ -25,6 +25,7 @@ import com.google.idea.blaze.base.run.targetfinder.TargetFinder; import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import javax.annotation.Nullable; @@ -58,7 +59,7 @@ protected ConfigurationFactory getConfigurationFactory() { } @Override - public void setupConfiguration(RunConfiguration configuration, Label label) { + public void setupConfiguration(DataContext context, RunConfiguration configuration, Label label) { BlazeCommandRunConfiguration blazeConfig = (BlazeCommandRunConfiguration) configuration; TargetInfo target = findProjectTarget(configuration.getProject(), label); blazeConfig.setTargetInfo(target); @@ -66,11 +67,12 @@ public void setupConfiguration(RunConfiguration configuration, Label label) { return; } - BlazeCommandRunConfigurationCommonState state = - blazeConfig.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class); + final var state = blazeConfig.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class); if (state != null) { + state.readContext(context); state.getCommandState().setCommand(commandForRuleType(target.getRuleType())); } + blazeConfig.setGeneratedName(); } diff --git a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java index 9e40076c77c..e4a77f5fe2c 100644 --- a/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java +++ b/base/src/com/google/idea/blaze/base/run/BlazeRunConfigurationFactory.java @@ -21,6 +21,8 @@ import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.configurations.ConfigurationFactory; import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.impl.SimpleDataContext; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; @@ -43,7 +45,7 @@ public RunnerAndConfigurationSettings createForTarget( Project project, RunManager runManager, Label target) { ConfigurationFactory factory = getConfigurationFactory(); RunConfiguration configuration = factory.createTemplateConfiguration(project, runManager); - setupConfiguration(configuration, target); + setupConfiguration(SimpleDataContext.EMPTY_CONTEXT, configuration, target); return runManager.createConfiguration(configuration, factory); } @@ -51,5 +53,5 @@ public RunnerAndConfigurationSettings createForTarget( protected abstract ConfigurationFactory getConfigurationFactory(); /** Initialize the configuration for the given target. */ - public abstract void setupConfiguration(RunConfiguration configuration, Label target); + public abstract void setupConfiguration(DataContext context, RunConfiguration configuration, Label target); } diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java index d194097d6f4..903249b9d58 100644 --- a/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java +++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeBuildFileRunConfigurationProducer.java @@ -64,7 +64,7 @@ protected boolean doSetupConfigFromContext( return false; } sourceElement.set(target.rule()); - setupConfiguration(configuration.getProject(), blazeProjectData, configuration, target); + setupConfiguration(configuration.getProject(), context, blazeProjectData, configuration, target); return configuration.getHandler().getCommandName() != null; } @@ -89,20 +89,20 @@ protected boolean doIsConfigFromContext( // we consider it close enough. The suggested name is checked because it tends // to cover what the handler considers important, // and ignores changes the user may have made to the name. - BlazeProjectData blazeProjectData = - BlazeProjectDataManager.getInstance(configuration.getProject()).getBlazeProjectData(); + final var blazeProjectData = BlazeProjectDataManager.getInstance(configuration.getProject()).getBlazeProjectData(); if (blazeProjectData == null) { return false; } - BlazeCommandRunConfiguration generatedConfiguration = - new BlazeCommandRunConfiguration( - configuration.getProject(), configuration.getFactory(), configuration.getName()); - setupConfiguration( - configuration.getProject(), blazeProjectData, generatedConfiguration, target); + + final var generatedConfiguration = new BlazeCommandRunConfiguration( + configuration.getProject(), + configuration.getFactory(), + configuration.getName() + ); + setupConfiguration(configuration.getProject(), context, blazeProjectData, generatedConfiguration, target); // ignore filtered test configs, produced by other configuration producers. - BlazeCommandRunConfigurationCommonState handlerState = - configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class); + final var handlerState = configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class); if (handlerState != null && handlerState.getTestFilterFlag() != null) { return false; } @@ -134,17 +134,21 @@ static BuildTarget getBuildTarget(@Nullable FuncallExpression rule) { private static void setupConfiguration( Project project, + ConfigurationContext context, BlazeProjectData blazeProjectData, BlazeCommandRunConfiguration configuration, BuildTarget target) { // First see if a BlazeRunConfigurationFactory can give us a specialized setup. - for (BlazeRunConfigurationFactory configurationFactory : - BlazeRunConfigurationFactory.EP_NAME.getExtensions()) { - if (configurationFactory.handlesTarget(project, blazeProjectData, target.label()) - && configurationFactory.handlesConfiguration(configuration)) { - configurationFactory.setupConfiguration(configuration, target.label()); - return; + for (final var configurationFactory : BlazeRunConfigurationFactory.EP_NAME.getExtensionList()) { + if (!configurationFactory.handlesTarget(project, blazeProjectData, target.label())) { + continue; } + if (!configurationFactory.handlesConfiguration(configuration)) { + continue; + } + + configurationFactory.setupConfiguration(context.getDataContext(), configuration, target.label()); + return; } // If no factory exists, directly set up the configuration. diff --git a/base/src/com/google/idea/blaze/base/run/producers/BlazeRunConfigurationProducer.java b/base/src/com/google/idea/blaze/base/run/producers/BlazeRunConfigurationProducer.java index 5f423104756..7049835ef93 100644 --- a/base/src/com/google/idea/blaze/base/run/producers/BlazeRunConfigurationProducer.java +++ b/base/src/com/google/idea/blaze/base/run/producers/BlazeRunConfigurationProducer.java @@ -26,16 +26,14 @@ import com.intellij.psi.PsiElement; /** Base class for Blaze run configuration producers. */ -public abstract class BlazeRunConfigurationProducer - extends RunConfigurationProducer { +public abstract class BlazeRunConfigurationProducer extends RunConfigurationProducer { protected BlazeRunConfigurationProducer(ConfigurationType configurationType) { super(configurationType); } @Override - public boolean isPreferredConfiguration( - ConfigurationFromContext self, ConfigurationFromContext other) { + public boolean isPreferredConfiguration(ConfigurationFromContext self, ConfigurationFromContext other) { return Blaze.isBlazeProject(self.getConfiguration().getProject()); } @@ -58,7 +56,9 @@ protected final boolean setupConfigurationFromContext( } protected abstract boolean doSetupConfigFromContext( - T configuration, ConfigurationContext context, Ref sourceElement); + T configuration, + ConfigurationContext context, + Ref sourceElement); @Override public final boolean isConfigurationFromContext(T configuration, ConfigurationContext context) { diff --git a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java index 6865af7afde..20dd3cc1a22 100644 --- a/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java +++ b/base/src/com/google/idea/blaze/base/run/state/BlazeCommandRunConfigurationCommonState.java @@ -23,6 +23,7 @@ import com.google.idea.blaze.base.settings.BuildSystemName; import com.intellij.execution.configurations.RuntimeConfigurationError; import com.intellij.execution.configurations.RuntimeConfigurationException; +import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.project.Project; import java.io.File; import javax.annotation.Nullable; @@ -32,8 +33,9 @@ * com.google.idea.blaze.base.run.confighandler.BlazeCommandRunConfigurationHandler} types. */ public class BlazeCommandRunConfigurationCommonState extends RunConfigurationCompositeState { - private static final String USER_BLAZE_FLAG_TAG = "blaze-user-flag"; - private static final String USER_EXE_FLAG_TAG = "blaze-user-exe-flag"; + public static final DataKey USER_BLAZE_FLAG = DataKey.create("blaze-user-flag"); + public static final DataKey USER_EXE_FLAG = DataKey.create("blaze-user-exe-flag"); + private static final String TEST_FILTER_FLAG_PREFIX = BlazeFlags.TEST_FILTER + '='; protected final BlazeCommandState command; @@ -44,8 +46,8 @@ public class BlazeCommandRunConfigurationCommonState extends RunConfigurationCom public BlazeCommandRunConfigurationCommonState(BuildSystemName buildSystemName) { command = new BlazeCommandState(); - blazeFlags = new RunConfigurationFlagsState(USER_BLAZE_FLAG_TAG, buildSystemName + " flags:"); - exeFlags = new RunConfigurationFlagsState(USER_EXE_FLAG_TAG, "Executable flags:"); + blazeFlags = new RunConfigurationFlagsState(USER_BLAZE_FLAG, buildSystemName + " flags:"); + exeFlags = new RunConfigurationFlagsState(USER_EXE_FLAG, "Executable flags:"); envVars = new EnvironmentVariablesState(); blazeBinary = new BlazeBinaryState(); } diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java index 88cee78efd6..6391c222b19 100644 --- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java +++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationCompositeState.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList; import com.google.idea.blaze.base.ui.UiUtil; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; @@ -45,6 +46,13 @@ protected ImmutableList getStates() { return states; } + @Override + public final void readContext(DataContext context) throws InvalidDataException { + for (RunConfigurationState state : getStates()) { + state.readContext(context); + } + } + @Override public final void readExternal(Element element) throws InvalidDataException { for (RunConfigurationState state : getStates()) { diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java index 1156db4b22e..4ee743249e1 100644 --- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java +++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationFlagsState.java @@ -21,7 +21,10 @@ import com.google.idea.blaze.base.command.BlazeFlags; import com.google.idea.blaze.base.execution.BlazeParametersListUtil; import com.google.idea.blaze.base.ui.UiUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.execution.ParametersListUtil; import java.awt.Container; @@ -39,23 +42,22 @@ /** State for a list of user-defined flags. */ public final class RunConfigurationFlagsState implements RunConfigurationState { - private final String tag; + private final DataKey key; private final String fieldLabel; /** Unprocessed flags, as the user entered them, tokenised on unquoted whitespace. */ private ImmutableList flags = ImmutableList.of(); - public RunConfigurationFlagsState(String tag, String fieldLabel) { - this.tag = tag; + public RunConfigurationFlagsState(DataKey key, String fieldLabel) { + this.key = key; this.fieldLabel = fieldLabel; } /** Flags ready to be used directly as args for external processes. */ public List getFlagsForExternalProcesses() { - List processedFlags = - flags.stream() - .map(s -> ParametersListUtil.parse(s, false, true).get(0)) - .collect(Collectors.toList()); + final var processedFlags = flags.stream() + .flatMap(s -> ParametersListUtil.parse(s, false, true).stream()) + .collect(Collectors.toList()); return BlazeFlags.expandBuildFlags(processedFlags); } @@ -69,15 +71,15 @@ public void setRawFlags(List flags) { } public RunConfigurationFlagsState copy() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState(tag, fieldLabel); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, fieldLabel); state.setRawFlags(getRawFlags()); return state; } @Override public void readExternal(Element element) { - ImmutableList.Builder flagsBuilder = ImmutableList.builder(); - for (Element e : element.getChildren(tag)) { + final var flagsBuilder = ImmutableList.builder(); + for (Element e : element.getChildren(key.getName())) { String flag = e.getTextTrim(); if (flag != null && !flag.isEmpty()) { flagsBuilder.add(flag); @@ -86,11 +88,20 @@ public void readExternal(Element element) { flags = flagsBuilder.build(); } + @Override + public void readContext(DataContext ctx) throws InvalidDataException { + final var data = ctx.getData(key); + + if (data != null) { + flags = ImmutableList.copyOf(data); + } + } + @Override public void writeExternal(Element element) { - element.removeChildren(tag); + element.removeChildren(key.getName()); for (String flag : flags) { - Element child = new Element(tag); + Element child = new Element(key.getName()); child.setText(flag); element.addContent(child); } diff --git a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationState.java b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationState.java index decb5b3e521..4f6ea18ad6c 100644 --- a/base/src/com/google/idea/blaze/base/run/state/RunConfigurationState.java +++ b/base/src/com/google/idea/blaze/base/run/state/RunConfigurationState.java @@ -15,6 +15,7 @@ */ package com.google.idea.blaze.base.run.state; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; @@ -23,6 +24,9 @@ /** Supports managing part of a run configuration's state. */ public interface RunConfigurationState { + /** Loads this handler's state from the context data. */ + default void readContext(DataContext ctx) throws InvalidDataException {} + /** Loads this handler's state from the external data. */ void readExternal(Element element) throws InvalidDataException; diff --git a/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java b/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java index 19462dec674..7791082c807 100644 --- a/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java +++ b/base/tests/unittests/com/google/idea/blaze/base/run/state/RunConfigurationFlagStateTest.java @@ -17,6 +17,7 @@ import static com.google.common.truth.Truth.assertThat; +import com.intellij.openapi.actionSystem.DataKey; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.idea.blaze.base.run.state.RunConfigurationFlagsState.RunConfigurationFlagsStateEditor; @@ -30,12 +31,15 @@ /** Unit tests for {@link RunConfigurationFlagsState}. */ @RunWith(JUnit4.class) public class RunConfigurationFlagStateTest { + + private static final DataKey key = DataKey.create("tag"); + @Test public void testEscapedDoubleQuotesRetainedAfterReserialization() { // previously, we were removing escape chars and quotes during ParametersListUtil.parse, then // not putting them back when converting back to a string. ImmutableList flags = ImmutableList.of("--flag=\\\"Hello_world!\\\"", "--flag2"); - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); state.setRawFlags(flags); RunConfigurationStateEditor editor = state.getEditor(null); @@ -51,7 +55,7 @@ public void testEscapedDoubleQuotesRetainedAfterReserialization() { @Test public void testEscapedSingleQuotesRetainedAfterReserialization() { ImmutableList flags = ImmutableList.of("--flag=\\'Hello_world!\\'", "--flag2"); - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); state.setRawFlags(flags); RunConfigurationStateEditor editor = state.getEditor(null); @@ -67,7 +71,7 @@ public void testEscapedSingleQuotesRetainedAfterReserialization() { @Test public void testQuotesRetainedAfterReserialization() { ImmutableList flags = ImmutableList.of("\"--flag=test\""); - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); state.setRawFlags(flags); RunConfigurationStateEditor editor = state.getEditor(null); @@ -80,7 +84,7 @@ public void testQuotesRetainedAfterReserialization() { @Test public void testDoubleQuotesInEditor() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); RunConfigurationStateEditor editor = state.getEditor(null); JTextArea textArea = getTextField(editor); @@ -106,7 +110,7 @@ public void testDoubleQuotesInEditor() { @Test public void testSingleQuotesInEditor() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); RunConfigurationStateEditor editor = state.getEditor(null); JTextArea textArea = getTextField(editor); @@ -131,7 +135,7 @@ public void testSingleQuotesInEditor() { @Test public void testNestedQuotesRetainedAfterRoundTripSerialization() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); RunConfigurationStateEditor editor = state.getEditor(null); JTextArea textArea = getTextField(editor); @@ -148,7 +152,7 @@ public void testNestedQuotesRetainedAfterRoundTripSerialization() { @Test public void testSplitOnWhitespaceAndNewlines() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); RunConfigurationStateEditor editor = state.getEditor(null); JTextArea textArea = getTextField(editor); @@ -172,7 +176,7 @@ public void testSplitOnWhitespaceAndNewlines() { @Test public void testFlagsContainingQuotedNewlines() { - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); RunConfigurationStateEditor editor = state.getEditor(null); JTextArea textArea = getTextField(editor); @@ -200,7 +204,7 @@ public void testNormalFlagsAreNotMangled() { "--experimental_show_artifacts", "--test_filter=com.google.idea.blaze.base.run.state.RunConfigurationFlagStateTest#", "--define=ij_product=intellij-latest"); - RunConfigurationFlagsState state = new RunConfigurationFlagsState("tag", "field"); + RunConfigurationFlagsState state = new RunConfigurationFlagsState(key, "field"); state.setRawFlags(flags); RunConfigurationStateEditor editor = state.getEditor(null); diff --git a/clwb/BUILD b/clwb/BUILD index 073e0d10b91..ff62d28d30a 100644 --- a/clwb/BUILD +++ b/clwb/BUILD @@ -187,7 +187,7 @@ clwb_headless_test( clwb_headless_test( name = "execution_headless_test", srcs = ["tests/headlesstests/com/google/idea/blaze/clwb/ExecutionTest.java"], - project = "simple", + project = "execution", ) clwb_headless_test( diff --git a/clwb/gdbserver b/clwb/gdbserver index 54d9d0df172..8ad1baf2507 100755 --- a/clwb/gdbserver +++ b/clwb/gdbserver @@ -35,6 +35,25 @@ if [[ -r ~/.gdbserver_wrapper_options ]]; then source ~/.gdbserver_wrapper_options fi +gdbserver_wrapper::version_lt() { + # compare versions like 9.1 < 10.1 using sort -V + [[ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" != "$2" ]] +} + +gdbserver_wrapper::detect_version() { + # try running `gdbserver --version`, then grep the first X or X.Y occurrence + if ver="$("$1" --version 2>/dev/null | grep -Eo '[0-9]+(\.[0-9]+)?' | head -n1)"; then + if [[ -n "$ver" ]]; then + echo "$ver" + return 0 + fi + fi + + # fallback to "0.0" if detection fails (to be conservative and enable escaping) + echo "0.0" + return 0 +} + gdbserver_wrapper::setup() { # create work directory for all temporary files work_directory="$(mktemp --tmpdir --directory "gdbserver_wrapper.XXXXXXXX")" @@ -92,8 +111,12 @@ gdbserver_wrapper::parse_args() { # the rest are params for inferior inferior=2 else - # do shell escaping via printf '%q' - new_args+=("$(printf "%q" "${old_arg}")") + # for inferior args, optionally shell-escape them for older gdbserver versions + if [[ "${need_shell_escaping}" -eq 1 ]]; then + new_args+=("$(printf "%q" "${old_arg}")") + else + new_args+=("${old_arg}") + fi fi done } @@ -160,11 +183,23 @@ gdbserver_wrapper::main() { local inferior_stderr inferior_stdout local redirection_wrapper local keep_work_directory verbose + local gdbserver_version need_shell_escaping + keep_work_directory="${GDBSERVER_WRAPPER_KEEP_WORK_DIRECTORY}" verbose="${GDB_WRAPPER_VERBOSE}" original_args=("$@") + # detect version from the first arg (gdbserver binary) + gdbserver_version="$(gdbserver_wrapper::detect_version "${original_args[0]}")" + + # enable shell escaping for gdbserver versions older than 10.1 + if gdbserver_wrapper::version_lt "${gdbserver_version}" "10.1"; then + need_shell_escaping=1 + else + need_shell_escaping=0 + fi + gdbserver_wrapper::setup gdbserver_wrapper::setup_redirection_wrapper diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java index efca9e16eeb..169c54df3f2 100644 --- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java +++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrLauncher.java @@ -26,8 +26,10 @@ import com.google.idea.blaze.base.command.buildresult.BuildResultHelper; import com.google.idea.blaze.base.command.buildresult.BuildResultHelperBep; import com.google.idea.blaze.base.console.BlazeConsoleLineProcessorProvider; +import com.google.idea.blaze.base.ideinfo.TargetIdeInfo; import com.google.idea.blaze.base.issueparser.ToolWindowTaskIssueOutputFilter; import com.google.idea.blaze.base.logging.EventLoggingService; +import com.google.idea.blaze.base.model.primitives.Label; import com.google.idea.blaze.base.model.primitives.TargetExpression; import com.google.idea.blaze.base.model.primitives.WorkspaceRoot; import com.google.idea.blaze.base.projectview.ProjectViewManager; @@ -45,6 +47,7 @@ import com.google.idea.blaze.base.settings.Blaze; import com.google.idea.blaze.base.settings.BlazeUserSettings; import com.google.idea.blaze.base.settings.BuildSystemName; +import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager; import com.google.idea.blaze.clwb.ToolchainUtils; import com.google.idea.blaze.cpp.CppBlazeRules; import com.intellij.execution.ExecutionException; @@ -76,6 +79,7 @@ import com.jetbrains.cidr.execution.testing.google.CidrGoogleTestConsoleProperties; import java.io.File; import java.util.List; +import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; @@ -227,6 +231,24 @@ private void updateCommandlineWithEnvironmentData(GeneralCommandLine commandLine commandLine.getEnvironment().putAll(envState.getEnvs()); } + private ImmutableList getTargetArguments(TargetExpression target) { + if (!(target instanceof Label label)) { + return ImmutableList.of(); + } + + final var projectData = BlazeProjectDataManager.getInstance(project).getBlazeProjectData(); + if (projectData == null) { + return ImmutableList.of(); + } + + return projectData.getTargetMap().get(label) + .stream() + .map(TargetIdeInfo::getcIdeInfo) + .filter(Objects::nonNull) + .flatMap(it -> it.getArgs().stream()) + .collect(ImmutableList.toImmutableList()); + } + @Override public CidrDebugProcess createDebugProcess(CommandLineState state, XDebugSession session) throws ExecutionException { @@ -255,6 +277,7 @@ public CidrDebugProcess createDebugProcess(CommandLineState state, XDebugSession GeneralCommandLine commandLine = new GeneralCommandLine(runner.executableToDebug.getPath()).withWorkDirectory(workingDir); + commandLine.addParameters(getTargetArguments(target)); commandLine.addParameters(handlerState.getExeFlagsState().getFlagsForExternalProcesses()); commandLine.addParameters(handlerState.getTestArgs()); diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRemoteDebugProcess.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRemoteDebugProcess.java index eb92ee6bd28..e430f8f3f2e 100644 --- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRemoteDebugProcess.java +++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRemoteDebugProcess.java @@ -21,7 +21,6 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; -import com.intellij.util.SystemProperties; import com.intellij.xdebugger.XDebugSession; import com.jetbrains.cidr.execution.TrivialInstaller; import com.jetbrains.cidr.execution.TrivialRunParameters; @@ -88,4 +87,8 @@ protected Inferior doLoadTarget(DebuggerDriver debuggerDriver) throws ExecutionE new File(remoteDebugParameters.getSysroot()), remoteDebugParameters.driverPathMapping()); } + + public ProcessHandler getTargetProcess() { + return targetProcess; + } } diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java index 3429fd59d15..0dab31f6f28 100644 --- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java +++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeCidrRunConfigurationRunner.java @@ -40,7 +40,6 @@ import com.intellij.execution.runners.ExecutionUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; -import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.util.PathUtil; import com.jetbrains.cidr.execution.CidrCommandLineState; @@ -162,7 +161,7 @@ private File getExecutableToDebug(ExecutionEnvironment env) throws ExecutionExce "More than 1 executable was produced when building %s; don't know which to debug", target)); } - LocalFileSystem.getInstance().refreshIoFiles(ImmutableList.of(file)); + return file; } catch (InterruptedException | CancellationException e) { streamProviderFuture.cancel(true); diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.java b/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.java deleted file mode 100644 index 5b29707da14..00000000000 --- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2019 The Bazel Authors. All rights reserved. - * - * 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.google.idea.blaze.clwb.run; - -import com.google.common.collect.ImmutableList; -import com.google.idea.blaze.base.command.BlazeCommandName; -import com.google.idea.blaze.base.command.BlazeFlags; -import com.google.idea.blaze.base.run.state.RunConfigurationState; -import com.google.idea.blaze.clwb.ToolchainUtils; -import com.google.idea.common.experiments.BoolExperiment; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.NotNullLazyValue; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.registry.Registry; -import com.intellij.util.PathUtil; -import com.jetbrains.cidr.cpp.toolchains.CPPDebugger; -import com.jetbrains.cidr.cpp.toolchains.CPPToolchains; -import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager; -import java.io.File; -import javax.annotation.Nullable; - -/** CLion-specific class that provides the slightly customized Toolchain for use with gdbserver */ -public class BlazeGDBServerProvider { - private static final Logger logger = Logger.getInstance(BlazeGDBServerProvider.class); - - /** - * This is a script distributed with the plugin that makes gdbserver behave more like how the - * environment expects. It will respond to signals, exit with the same exit code as the inferior, - * and escape the parameters correctly. - */ - private static final NotNullLazyValue GDBSERVER_WRAPPER = - new NotNullLazyValue() { - @Override - protected String compute() { - String jarPath = PathUtil.getJarPathForClass(BlazeCidrLauncher.class); - File pluginrootDirectory = new File(jarPath).getParentFile().getParentFile(); - return new File(pluginrootDirectory, "gdb/gdbserver").getPath(); - } - }; - - private static final BoolExperiment useRemoteDebuggingWrapper = - new BoolExperiment("cc.remote.debugging.wrapper", true); - - // These flags are used when debugging cc_binary targets when remote debugging - // is enabled (cc.remote.debugging) - private static final ImmutableList EXTRA_FLAGS_FOR_DEBUG_RUN = - ImmutableList.of( - "--compilation_mode=dbg", "--strip=never", "--dynamic_mode=off"); - - // These flags are used when debugging cc_test targets when remote debugging - // is enabled (cc.remote.debugging) - private static final ImmutableList EXTRA_FLAGS_FOR_DEBUG_TEST = - ImmutableList.of( - "--compilation_mode=dbg", - "--strip=never", - "--dynamic_mode=off", - "--test_timeout=3600", - BlazeFlags.NO_CACHE_TEST_RESULTS, - BlazeFlags.EXCLUSIVE_TEST_EXECUTION, - BlazeFlags.DISABLE_TEST_SHARDING); - - // Allows the fission flag to be disabled as workaround for - // https://github.com/bazelbuild/intellij/issues/5604 - static ImmutableList getOptionalFissionArguments() { - if(Registry.is("bazel.clwb.debug.fission.disabled")) { - return ImmutableList.of(); - } else { - return ImmutableList.of("--fission=yes"); - } - } - - static ImmutableList getFlagsForDebugging(RunConfigurationState state) { - if (!(state instanceof BlazeCidrRunConfigState)) { - return ImmutableList.of(); - } - BlazeCidrRunConfigState handlerState = (BlazeCidrRunConfigState) state; - BlazeCommandName commandName = handlerState.getCommandState().getCommand(); - ImmutableList.Builder builder = ImmutableList.builder(); - - CPPToolchains.Toolchain toolchain = ToolchainUtils.getToolchain(); - String gdbServerPath = BlazeGDBServerProvider.getGDBServerPath(toolchain); - if (gdbServerPath == null) { - // couldn't find it, fall back to trying PATH - gdbServerPath = "gdbserver"; - } - - if (useRemoteDebuggingWrapper.getValue()) { - String runUnderOption = - String.format( - "--run_under='%s' '%s' '%s' --once localhost:%d --target", - "bash", - GDBSERVER_WRAPPER.getValue(), - gdbServerPath, - handlerState.getDebugPortState().port); - builder.add(runUnderOption); - } else { - String runUnderOption = - String.format( - "--run_under='%s' --once localhost:%d", - gdbServerPath, handlerState.getDebugPortState().port); - builder.add(runUnderOption); - } - if (BlazeCommandName.RUN.equals(commandName)) { - builder.addAll(EXTRA_FLAGS_FOR_DEBUG_RUN); - builder.addAll(getOptionalFissionArguments()); - return builder.build(); - } - if (BlazeCommandName.TEST.equals(commandName)) { - builder.addAll(EXTRA_FLAGS_FOR_DEBUG_TEST); - builder.addAll(getOptionalFissionArguments()); - return builder.build(); - } - return ImmutableList.of(); - } - - @Nullable - private static String getGDBServerPath(CPPToolchains.Toolchain toolchain) { - String gdbPath; - - // TODO: this still depends on the default toolchain - CPPDebugger.Kind debuggerKind = toolchain.getDebuggerKind(); - switch (debuggerKind) { - case CUSTOM_GDB: - gdbPath = toolchain.getCustomGDBExecutablePath(); - break; - case BUNDLED_GDB: - gdbPath = CidrDebuggerPathManager.getBundledGDBBinary().getPath(); - break; - default: - logger.error("Trying to resolve gdbserver executable for " + debuggerKind.toString()); - return null; - } - - // We are going to just try to append "server" to the gdb executable path - it would be nicer - // to have this stored as part of the toolchain configuration, but it isn't. - File gdbServer = new File(gdbPath + "server"); - if (!gdbServer.exists()) { - return null; - } - return gdbServer.getAbsolutePath(); - } - -} diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt b/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt new file mode 100644 index 00000000000..a1afd3978f7 --- /dev/null +++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2019 The Bazel Authors. All rights reserved. + * + * 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.google.idea.blaze.clwb.run + +import com.google.common.collect.ImmutableList +import com.google.idea.blaze.base.command.BlazeCommandName +import com.google.idea.blaze.base.command.BlazeFlags +import com.google.idea.blaze.base.run.state.RunConfigurationState +import com.google.idea.blaze.clwb.ToolchainUtils +import com.google.idea.common.experiments.BoolExperiment +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.util.registry.Registry +import com.intellij.util.PathUtil +import com.jetbrains.cidr.cpp.toolchains.CPPDebugger +import com.jetbrains.cidr.cpp.toolchains.CPPToolchains +import com.jetbrains.cidr.execution.debugger.CidrDebuggerPathManager +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.absolutePathString + +private val LOG = logger() + +private val USE_REMOTE_DEBUGGING_WRAPPER: BoolExperiment = BoolExperiment("cc.remote.debugging.wrapper", true) + +private const val GDB_SERVER_PROPERTY = "clwb.gdbserverPath" + +/** CLion-specific class that provides the slightly customized Toolchain for use with gdbserver */ +object BlazeGDBServerProvider { + + /** + * This is a script distributed with the plugin that makes gdbserver behave more like how the + * environment expects. It will respond to signals, exit with the same exit code as the inferior, + * and escape the parameters correctly. + */ + private val GDBSERVER_WRAPPER: String by lazy { + if (System.getProperty(GDB_SERVER_PROPERTY) != null) { + Path.of(System.getProperty(GDB_SERVER_PROPERTY)).absolutePathString() + } else { + val jarPath = Path.of(PathUtil.getJarPathForClass(BlazeCidrLauncher::class.java)) + jarPath.parent.parent.resolve("gdb").resolve("gdbserver").toString() + } + } + + // These flags are used when debugging cc_binary targets when remote debugging + // is enabled (cc.remote.debugging) + private val EXTRA_FLAGS_FOR_DEBUG_RUN = ImmutableList.of( + "--compilation_mode=dbg", "--strip=never", "--dynamic_mode=off" + ) + + // These flags are used when debugging cc_test targets when remote debugging + // is enabled (cc.remote.debugging) + private val EXTRA_FLAGS_FOR_DEBUG_TEST = ImmutableList.of( + "--compilation_mode=dbg", + "--strip=never", + "--dynamic_mode=off", + "--test_timeout=3600", + BlazeFlags.NO_CACHE_TEST_RESULTS, + BlazeFlags.EXCLUSIVE_TEST_EXECUTION, + BlazeFlags.DISABLE_TEST_SHARDING + ) + + // Allows the fission flag to be disabled as workaround for + @JvmStatic + fun getOptionalFissionArguments(): ImmutableList { + return if (Registry.`is`("bazel.clwb.debug.fission.disabled")) { + ImmutableList.of() + } else { + ImmutableList.of("--fission=yes") + } + } + + @JvmStatic + fun getFlagsForDebugging(state: RunConfigurationState?): ImmutableList { + if (state !is BlazeCidrRunConfigState) { + return ImmutableList.of() + } + + val commandName = state.commandState.command + val builder = ImmutableList.builder() + + val toolchain = ToolchainUtils.getToolchain() + + // if gdbserver could not be found, fall back to trying PATH + val gdbServerPath = getGDBServerPath(toolchain) ?: "gdbserver" + + if (USE_REMOTE_DEBUGGING_WRAPPER.value) { + builder.add( + String.format( + "--run_under='bash' '%s' '%s' --once localhost:%d --target", + GDBSERVER_WRAPPER, + gdbServerPath, + state.getDebugPortState().port, + ) + ) + } else { + builder.add( + String.format( + "--run_under='%s' --once localhost:%d", + gdbServerPath, + state.getDebugPortState().port, + ) + ) + } + + if (BlazeCommandName.RUN == commandName) { + builder.addAll(EXTRA_FLAGS_FOR_DEBUG_RUN) + builder.addAll(getOptionalFissionArguments()) + return builder.build() + } + + if (BlazeCommandName.TEST == commandName) { + builder.addAll(EXTRA_FLAGS_FOR_DEBUG_TEST) + builder.addAll(getOptionalFissionArguments()) + return builder.build() + } + + return ImmutableList.of() + } + + private fun getGDBServerPath(toolchain: CPPToolchains.Toolchain): String? { + // TODO: this still depends on the default toolchain + val gdbPath = when (toolchain.debuggerKind) { + CPPDebugger.Kind.CUSTOM_GDB -> toolchain.customGDBExecutablePath + CPPDebugger.Kind.BUNDLED_GDB -> CidrDebuggerPathManager.getBundledGDBBinary().path + + else -> { + LOG.error("Trying to resolve gdbserver executable for ${toolchain.debuggerKind}") + return null + } + } + + // We are going to just try to append "server" to the gdb executable path - it would be nicer + // to have this stored as part of the toolchain configuration, but it isn't. + val gdbServer = Path.of(gdbPath + "server") + if (!Files.exists(gdbServer)) { + return null + } + + return gdbServer.absolutePathString() + } +} diff --git a/clwb/test_defs.bzl b/clwb/test_defs.bzl index c26da8c4d85..acb1815dea5 100644 --- a/clwb/test_defs.bzl +++ b/clwb/test_defs.bzl @@ -20,6 +20,8 @@ def _integration_test_suite(name, srcs, deps = []): "-Didea.suppressed.plugins.set.selector=classic", # enable detailed logging in tests to diagnose issues in CI "-Didea.log.trace.categories=com.jetbrains.cidr.lang.workspace,com.google.idea.blaze.cpp.BlazeCWorkspace", + # the location of the gdbserver wrapper script, required at runtime + "-Dclwb.gdbserverPath=$(rootpath //clwb:gdbserver_wrapper)", ], deps = deps + [ ":clwb_lib", @@ -34,6 +36,9 @@ def _integration_test_suite(name, srcs, deps = []): "@org_opentest4j_opentest4j//jar", "//testing/src/com/google/idea/testing/headless", ], + data = [ + "//clwb:gdbserver_wrapper", + ] ) def clwb_integration_test(name, srcs, deps = []): diff --git a/clwb/tests/headlesstests/com/google/idea/blaze/clwb/ExecutionTest.java b/clwb/tests/headlesstests/com/google/idea/blaze/clwb/ExecutionTest.java index 8051c1e7403..d4796f00f51 100644 --- a/clwb/tests/headlesstests/com/google/idea/blaze/clwb/ExecutionTest.java +++ b/clwb/tests/headlesstests/com/google/idea/blaze/clwb/ExecutionTest.java @@ -1,10 +1,15 @@ package com.google.idea.blaze.clwb; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import com.google.idea.blaze.base.bazel.BazelVersion; import com.google.idea.blaze.base.run.producers.BlazeBuildFileRunConfigurationProducer; +import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState; import com.google.idea.blaze.clwb.base.ClwbHeadlessTestCase; +import com.google.idea.blaze.clwb.run.BlazeCidrRemoteDebugProcess; import com.google.idea.blaze.common.Label; +import com.google.idea.testing.headless.ProjectViewBuilder; import com.intellij.execution.ExecutionListener; import com.intellij.execution.ExecutionManager; import com.intellij.execution.ExecutorRegistry; @@ -12,9 +17,13 @@ import com.intellij.execution.PsiLocation; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.execution.actions.RunConfigurationProducer; +import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.executors.DefaultRunExecutor; import com.intellij.execution.impl.ExecutionManagerImpl; +import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.process.ProcessListener; +import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionUtil; import com.intellij.openapi.actionSystem.ActionPlaces; @@ -22,7 +31,13 @@ import com.intellij.openapi.actionSystem.PlatformCoreDataKeys; import com.intellij.openapi.actionSystem.impl.SimpleDataContext; import com.intellij.openapi.module.ModuleUtilCore; -import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.openapi.util.Key; +import com.intellij.util.system.OS; +import com.intellij.xdebugger.XDebuggerManager; +import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; @@ -33,35 +48,99 @@ @RunWith(JUnit4.class) public class ExecutionTest extends ClwbHeadlessTestCase { + private static final String ECHO_OUTPUT_MARKER = "ECHO_OUTPUT_FILE: "; + @Test public void testClwb() throws Exception { final var errors = runSync(defaultSyncParams().build()); errors.assertNoErrors(); - checkRun(); - checkTest(); + checkRun(DefaultRunExecutor.EXECUTOR_ID); + checkArgs(DefaultRunExecutor.EXECUTOR_ID); + + // debugging on macOS requires special permissions which the CI agents do not have + if (!OS.CURRENT.equals(OS.macOS)) { + checkRun(DefaultDebugExecutor.EXECUTOR_ID); + checkArgs(DefaultDebugExecutor.EXECUTOR_ID); + } + } + + @Override + protected ProjectViewBuilder projectViewText(BazelVersion version) { + final var builder = super.projectViewText(version); + + // required for building with bazel 6 + if (OS.CURRENT.equals(OS.Windows)) { + builder.addBuildFlag("--cxxopt=/std:c++17"); + } else { + builder.addBuildFlag("--cxxopt=-std=c++17"); + } + + return builder; } - private void checkRun() throws Exception { - final var result = execute(Label.of("//main:hello-world"), DefaultRunExecutor.EXECUTOR_ID); - assertThat(result).isEqualTo(0); + private void checkRun(String executorId) throws Exception { + final var echo = execute(Label.of("//main:echo0"), executorId, ""); + echo.assertSuccess(); + + final var test = execute(Label.of("//main:test"), DefaultRunExecutor.EXECUTOR_ID, ""); + test.assertSuccess(); + } + + private void checkArgs(String executorId) throws Exception { + assertThat(executeEcho("echo0", executorId, "'one argument with spaces'")).containsExactly( + "one argument with spaces" + ); + assertThat(executeEcho("echo0", executorId, "'one argument' 'another argument'")).containsExactly( + "one argument", + "another argument" + ); + + assertThat(executeEcho("echo1", executorId, "CONFIG_ARGUMENT")).containsExactly( + "BUILD_FILE_STRING", + "CONFIG_ARGUMENT" + ); + + assertThat(executeEcho("echo2", executorId, "CONFIG_ARGUMENT")).containsExactly( + "main/echo.cc", + "CONFIG_ARGUMENT" + ); } - private void checkTest() throws Exception { - final var result = execute(Label.of("//main:test"), DefaultRunExecutor.EXECUTOR_ID); - assertThat(result).isEqualTo(0); + /** + * Executes the echo program and returns the program's output lines. The + * program simply writes all received arguments to a file. + */ + private List executeEcho(String target, String executorId, String args) throws Exception { + final var result = execute(Label.of("//main:" + target), executorId, args); + result.assertSuccess(); + + final var line = result.output().lines().filter((it) -> it.startsWith(ECHO_OUTPUT_MARKER)).findFirst(); + assertThat(line).isPresent(); + + final var path = Path.of(line.get().substring(ECHO_OUTPUT_MARKER.length())); + assertThat(Files.exists(path)).isTrue(); + + return Files.readAllLines(path); } - private int execute(Label label, String executorId) throws Exception { + /** + * Executes the run configuration for the given label with the given executor. + * The flags are set on the run configuration. Supported executors: + * - DefaultRunExecutor: just runs the binary or test + * - DefaultDebugExecutor: debugs the binary or test + */ + private ExecutionResult execute(Label label, String executorId, String args) throws Exception { final var element = findRule(label); final var context = ConfigurationContext.getFromContext(SimpleDataContext.builder() .add(CommonDataKeys.PROJECT, getProject()) .add(PlatformCoreDataKeys.MODULE, ModuleUtilCore.findModuleForPsiElement(element)) .add(Location.DATA_KEY, PsiLocation.fromPsiElement(element)) + .add(BlazeCommandRunConfigurationCommonState.USER_EXE_FLAG, new String[]{args}) .build(), ActionPlaces.UNKNOWN); - final var executor = ExecutorRegistry.getInstance().getExecutorById(DefaultRunExecutor.EXECUTOR_ID); + final var executor = ExecutorRegistry.getInstance().getExecutorById(executorId); assertThat(executor).isNotNull(); final var producer = RunConfigurationProducer.getInstance(BlazeBuildFileRunConfigurationProducer.class); @@ -71,10 +150,42 @@ private int execute(Label label, String executorId) throws Exception { final var environmentBuilder = ExecutionUtil.createEnvironment(executor, configuration.getConfigurationSettings()); assertThat(environmentBuilder).isNotNull(); - final var environment = environmentBuilder.build(); + final var manager = (ExecutionManagerImpl) ExecutionManager.getInstance(getProject()); + manager.setForceCompilationInTests(true); + manager.restartRunProfile(environmentBuilder.build()); final var future = new CompletableFuture(); + final var outputBuilder = new StringBuilder(); + + final var listener = new ProcessListener() { + public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { + if (outputType == ProcessOutputTypes.STDOUT) { + outputBuilder.append(event.getText()); + } + } + }; + getProject().getMessageBus().connect(getTestRootDisposable()).subscribe(ExecutionManager.EXECUTION_TOPIC, new ExecutionListener() { + @Override + public void processStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, @NotNull ProcessHandler handler) { + if (executorId.equals(DefaultRunExecutor.EXECUTOR_ID)) { + handler.addProcessListener(listener); + } + + if (executorId.equals(DefaultDebugExecutor.EXECUTOR_ID)) { + final var session = XDebuggerManager.getInstance(myProject).getCurrentSession(); + assertThat(session).isNotNull(); + + if (session.getDebugProcess() instanceof BlazeCidrRemoteDebugProcess process) { + process.getTargetProcess().addProcessListener(listener); + } else if (session.getDebugProcess() instanceof CidrLocalDebugProcess process) { + process.getProcessHandler().addProcessListener(listener); + } else { + future.completeExceptionally(new IllegalStateException("unexpected debug process type")); + } + } + } + @Override public void processNotStarted(@NotNull String executorId, @NotNull ExecutionEnvironment env, Throwable cause) { future.completeExceptionally(cause); @@ -86,10 +197,14 @@ public void processTerminated(@NotNull String executorId, @NotNull ExecutionEnvi } }); - final var manager = (ExecutionManagerImpl) ExecutionManager.getInstance(getProject()); - manager.setForceCompilationInTests(true); - manager.restartRunProfile(environment); + final var exitCode = pullFuture(future, 2, TimeUnit.MINUTES); + return new ExecutionResult(outputBuilder.toString(), exitCode == null ? -1 : exitCode); + } + + private record ExecutionResult(String output, int exitCode) { - return pullFuture(future, 2, TimeUnit.MINUTES); + void assertSuccess() { + assertWithMessage(output).that(exitCode).isEqualTo(0); + } } } diff --git a/clwb/tests/headlesstests/com/google/idea/blaze/clwb/base/ClwbHeadlessTestCase.java b/clwb/tests/headlesstests/com/google/idea/blaze/clwb/base/ClwbHeadlessTestCase.java index 59872a356cc..065c4e56123 100644 --- a/clwb/tests/headlesstests/com/google/idea/blaze/clwb/base/ClwbHeadlessTestCase.java +++ b/clwb/tests/headlesstests/com/google/idea/blaze/clwb/base/ClwbHeadlessTestCase.java @@ -58,12 +58,6 @@ private void setupSandboxBin() { protected void addAllowedVfsRoots(ArrayList roots) { } - @Override - protected ProjectViewBuilder projectViewText(BazelVersion version) { - // required for Bazel 6 integration tests - return super.projectViewText(version).addBuildFlag("--cxxopt=-std=c++17"); - } - protected OCWorkspace getWorkspace() { return OCWorkspace.getInstance(myProject); } diff --git a/clwb/tests/projects/execution/MODULE.bazel b/clwb/tests/projects/execution/MODULE.bazel new file mode 100644 index 00000000000..cc0330ea9a6 --- /dev/null +++ b/clwb/tests/projects/execution/MODULE.bazel @@ -0,0 +1,2 @@ +bazel_dep(name = "rules_cc", version = "0.1.4") +bazel_dep(name = "catch2", version = "3.7.1") diff --git a/clwb/tests/projects/execution/main/BUILD b/clwb/tests/projects/execution/main/BUILD new file mode 100644 index 00000000000..8a531320c19 --- /dev/null +++ b/clwb/tests/projects/execution/main/BUILD @@ -0,0 +1,24 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_test") + +cc_binary( + name = "echo0", + srcs = ["echo.cc"], +) + +cc_binary( + name = "echo1", + srcs = ["echo.cc"], + args = ["BUILD_FILE_STRING"], +) + +cc_binary( + name = "echo2", + srcs = ["echo.cc"], + args = ["$(location :echo.cc)"], +) + +cc_test( + name = "test", + srcs = ["test.cc"], + deps = ["@catch2//:catch2_main"], +) diff --git a/clwb/tests/projects/execution/main/echo.cc b/clwb/tests/projects/execution/main/echo.cc new file mode 100644 index 00000000000..b8b597f4d1e --- /dev/null +++ b/clwb/tests/projects/execution/main/echo.cc @@ -0,0 +1,19 @@ +#include +#include +#include + +int main(int argc, char* argv[]) { + std::ofstream outputFile("output.txt"); + if (!outputFile) { + return 1; + } + + for (int i = 1; i < argc; ++i) { + outputFile << argv[i] << std::endl; + } + + auto fullPath = std::filesystem::current_path() / "output.txt"; + std::cout << "ECHO_OUTPUT_FILE: " << fullPath.string() << std::endl; + + return 0; +} diff --git a/clwb/tests/projects/execution/main/test.cc b/clwb/tests/projects/execution/main/test.cc new file mode 100644 index 00000000000..c6fd211d423 --- /dev/null +++ b/clwb/tests/projects/execution/main/test.cc @@ -0,0 +1,5 @@ +#include + +TEST_CASE("0") { + REQUIRE(0 == 0); +} \ No newline at end of file diff --git a/proto/intellij_ide_info.proto b/proto/intellij_ide_info.proto index 522de285038..2ad9de54513 100644 --- a/proto/intellij_ide_info.proto +++ b/proto/intellij_ide_info.proto @@ -66,6 +66,8 @@ message CIdeInfo { string include_prefix = 10; string strip_include_prefix = 11; + + repeated string args = 12; } message AndroidIdeInfo {