diff --git a/build_defs/BUILD b/build_defs/BUILD index 80f2d16c229..0f4be18324a 100644 --- a/build_defs/BUILD +++ b/build_defs/BUILD @@ -41,9 +41,3 @@ py_binary( srcs = ["package_meta_inf_files.py"], python_version = "PY3", ) - -py_binary( - name = "zip_plugin_files", - srcs = ["zip_plugin_files.py"], - python_version = "PY3", -) diff --git a/build_defs/build_defs.bzl b/build_defs/build_defs.bzl index 460dc4be1e6..528b54de881 100644 --- a/build_defs/build_defs.bzl +++ b/build_defs/build_defs.bzl @@ -190,146 +190,6 @@ def api_version_txt(name, check_eap, application_info_json = None, **kwargs): **kwargs ) -def _transition_impl(_, attr): - return {"//command_line_option:javacopt": attr.java_copts} - -_java_copts_transition = transition( - implementation = _transition_impl, - inputs = [], - outputs = ["//command_line_option:javacopt"], -) - -repackaged_files_data = provider() - -def _repackaged_files_impl(ctx): - prefix = ctx.attr.prefix - if prefix.startswith("/"): - fail("'prefix' must be a relative path") - input_files = depset() - for target in ctx.attr.srcs: - input_files = depset(transitive = [input_files, target.files]) - - return [ - # TODO(brendandouglas): Only valid for Bazel 0.5 onwards. Uncomment when - # 0.5 used more widely. - # DefaultInfo(files = input_files), - repackaged_files_data( - files = input_files, - prefix = prefix, - strip_prefix = ctx.attr.strip_prefix, - executable = ctx.attr.executable, - ), - ] - -_repackaged_files = rule( - implementation = _repackaged_files_impl, - attrs = { - "srcs": attr.label_list(mandatory = True, allow_files = True, cfg = _java_copts_transition), - "prefix": attr.string(mandatory = True), - "strip_prefix": attr.string(mandatory = True), - "executable": attr.bool(mandatory = False), - "java_copts": attr.string_list(default = []), - }, -) - -def repackaged_files(name, srcs = [], prefix = None, strip_prefix = ".", executable = False, java_copts = [], **kwargs): - """Assembles files together so that they can be packaged as an IntelliJ plugin. - - A cut-down version of the internal 'pkgfilegroup' rule. - - Args: - name: The name of this target - srcs: A list of targets which are dependencies of this rule. All output files of each of these - targets will be repackaged. - prefix: Where the package should install these files, relative to the 'plugins' directory. - strip_prefix: Which part of the input file path should be stripped prior to applying 'prefix'. - If ".", all subdirectories are stripped. If the empty string, the full package-relative path - is used. Default is "." - java_copts: Java compilation options for building the targets to package. - **kwargs: Any further arguments to be passed to the target - """ - _repackaged_files(name = name, srcs = srcs, prefix = prefix, strip_prefix = strip_prefix, executable = executable, java_copts = java_copts, **kwargs) - -def _strip_external_workspace_prefix(short_path): - """If this target is sitting in an external workspace, return the workspace-relative path.""" - if short_path.startswith("../") or short_path.startswith("external/"): - return "/".join(short_path.split("/")[2:]) - return short_path - -def output_path(f, repackaged_files_data): - """Returns the output path of a file, for a given set of repackaging parameters.""" - prefix = repackaged_files_data.prefix - strip_prefix = repackaged_files_data.strip_prefix - - short_path = _strip_external_workspace_prefix(f.short_path).strip("/") - - if strip_prefix == ".": - return prefix + "/" + f.basename - if strip_prefix == "": - return prefix + "/" + short_path - - strip_prefix = strip_prefix.strip("/") - old_path = short_path[:-len(f.basename)].strip("/") - if not old_path.startswith(strip_prefix): - fail("Invalid strip_prefix '%s': path actually starts with '%s'" % (strip_prefix, old_path)) - - stripped = old_path[len(strip_prefix):].strip("/") - if stripped == "": - return "%s/%s" % (prefix, f.basename) - return "%s/%s/%s" % (prefix, stripped, f.basename) - -def _plugin_deploy_zip_impl(ctx): - zip_name = ctx.attr.zip_filename - zip_file = ctx.actions.declare_file(zip_name) - - input_files = depset() - exec_path_to_zip_path = {} - for target in ctx.attr.srcs: - data = target[repackaged_files_data] - input_files = depset(transitive = [input_files, data.files]) - for f in data.files.to_list(): - exec_path_to_zip_path[f.path] = (output_path(f, data), data.executable) - - args = [] - args.extend(["--output", zip_file.path]) - for exec_path, (zip_path, exec) in exec_path_to_zip_path.items(): - args.extend([exec_path, zip_path, "True" if exec else "False"]) - ctx.actions.run( - executable = ctx.executable._zip_plugin_files, - arguments = args, - inputs = input_files.to_list(), - outputs = [zip_file], - mnemonic = "ZipPluginFiles", - progress_message = "Creating final plugin zip archive", - ) - files = depset([zip_file]) - return [DefaultInfo(files = files)] - -_plugin_deploy_zip = rule( - implementation = _plugin_deploy_zip_impl, - attrs = { - "srcs": attr.label_list(mandatory = True, providers = []), - "zip_filename": attr.string(mandatory = True), - "_zip_plugin_files": attr.label( - default = Label("//build_defs:zip_plugin_files"), - executable = True, - cfg = "exec", - ), - }, -) - -def plugin_deploy_zip(name, srcs, zip_filename, **kwargs): - """Packages up plugin files into a zip archive. - - Args: - name: The name of this target - srcs: A list of targets of type 'repackaged_files', specifying the input files and relative - paths to include in the output zip archive. - zip_filename: The output zip filename. - **kwargs: Any further arguments to be passed to the target - """ - _plugin_deploy_zip(name = name, zip_filename = zip_filename, srcs = srcs, **kwargs) - def combine_visibilities(*args): """ Concatenates the given lists of visibilities and returns the combined list. diff --git a/build_defs/common.bzl b/build_defs/common.bzl new file mode 100644 index 00000000000..13ed5d667c6 --- /dev/null +++ b/build_defs/common.bzl @@ -0,0 +1,28 @@ +load("@bazel_skylib//lib:paths.bzl", "paths") + +def _compute_plugin_layout(prefix, targets): + """Computes the plugin layout from the target list. + + All files of the targets are install into the lib directory, this should only be jars. Runfiles + of the targets are installed directly into the root directory. However, all symlink mappings + are respected. + """ + mapping = {} + + for target in targets: + info = target[DefaultInfo] + + for file in info.files.to_list(): + mapping[paths.join(prefix, "lib", file.basename)] = file + + for link in info.default_runfiles.symlinks.to_list(): + mapping[paths.join(prefix, link.path)] = link.target_file + + for file in info.default_runfiles.files.to_list(): + mapping[paths.join(prefix, file.path)] = file + + return mapping + +intellij_common = struct( + compute_plugin_layout = _compute_plugin_layout, +) diff --git a/build_defs/intellij_plugin.bzl b/build_defs/intellij_plugin.bzl index 226e5e25367..fef9f53f7e3 100644 --- a/build_defs/intellij_plugin.bzl +++ b/build_defs/intellij_plugin.bzl @@ -39,13 +39,6 @@ intellij_plugin( load("@rules_java//java:defs.bzl", "JavaInfo", "java_binary", "java_common", "java_import") load(":intellij_plugin_library.bzl", "OptionalPluginXmlInfo", "IntellijPluginLibraryInfo") -load( - "//build_defs:restrictions.bzl", - "RestrictedInfo", - "restricted_deps_aspect", - "validate_restrictions", - "validate_unchecked_internal", -) def _optional_plugin_xml_impl(ctx): attr = ctx.attr @@ -243,27 +236,13 @@ def _intellij_plugin_jar_impl(ctx): module_to_merged_xmls = _merge_optional_plugin_xmls(ctx) final_plugin_xml_file = _add_optional_dependencies_to_plugin_xml(ctx, augmented_xml, [k.name for k in module_to_merged_xmls.keys() if not k.is_synthetic]) jar_file = _package_meta_inf_files(ctx, final_plugin_xml_file, module_to_merged_xmls) - files = depset([jar_file]) - - if ctx.attr.restrict_deps: - dependencies = {} - unchecked_transitive = [] - roots = [] - for k in ctx.attr.restricted_deps: - if RestrictedInfo in k: - dependencies.update(k[RestrictedInfo].dependencies) - unchecked_transitive.append(k[RestrictedInfo].unchecked) - roots.append(k[RestrictedInfo].roots) - - # Uncomment the next line to see all buildable roots: - # fail("".join([" " + str(t) + "\n" for t in depset(transitive=roots).to_list()])) - validate_restrictions(dependencies) - unchecked = [str(t.label) for t in depset(direct = [], transitive = unchecked_transitive).to_list()] - validate_unchecked_internal(unchecked) - - return DefaultInfo( - files = files, - ) + + runfiles = ctx.runfiles().merge_all([dep[IntellijPluginLibraryInfo].runfiles for dep in ctx.attr.deps]) + + return [ + JavaInfo(output_jar = jar_file, compile_jar = jar_file), + DefaultInfo(files = depset([jar_file]), runfiles = runfiles), + ] _intellij_plugin_jar = rule( implementation = _intellij_plugin_jar_impl, @@ -273,8 +252,6 @@ _intellij_plugin_jar = rule( "optional_plugin_xmls": attr.label_list(providers = [OptionalPluginXmlInfo]), "jar_name": attr.string(mandatory = True), "deps": attr.label_list(providers = [[IntellijPluginLibraryInfo]]), - "restrict_deps": attr.bool(), - "restricted_deps": attr.label_list(aspects = [restricted_deps_aspect]), "plugin_icons": attr.label_list(allow_files = True), "_merge_xml_binary": attr.label( default = Label("//build_defs:merge_xml"), @@ -302,9 +279,7 @@ def intellij_plugin( jar_name = None, extra_runtime_deps = [], plugin_icons = [], - restrict_deps = False, tags = [], - target_compatible_with = [], testonly = 0, **kwargs): """Creates an intellij plugin from the given deps and plugin.xml. @@ -318,7 +293,6 @@ def intellij_plugin( extra_runtime_deps: runtime_deps added to java_binary or java_test calls plugin_icons: Plugin logo files to be placed in META-INF. Follow https://plugins.jetbrains.com/docs/intellij/plugin-icon-file.html#plugin-logo-requirements tags: Tags to add to generated rules - target_compatible_with: To be passed through to generated rules testonly: testonly setting for generated rules. **kwargs: Any further arguments to be passed to the final target """ @@ -335,7 +309,6 @@ def intellij_plugin( runtime_deps = [":" + java_deps_name] + extra_runtime_deps, create_executable = 0, tags = tags, - target_compatible_with = target_compatible_with, testonly = testonly, ) @@ -359,28 +332,14 @@ def intellij_plugin( message = "Applying workarounds to plugin jar", ) - jar_target_name = name + "_intellij_plugin_jar" _intellij_plugin_jar( - name = jar_target_name, + name = name, deploy_jar = deploy_jar, jar_name = jar_name or (name + ".jar"), deps = deps, - restrict_deps = restrict_deps, - restricted_deps = deps if restrict_deps else [], plugin_xml = plugin_xml, optional_plugin_xmls = optional_plugin_xmls, plugin_icons = plugin_icons, tags = tags, - target_compatible_with = target_compatible_with, - testonly = testonly, - ) - - # included (with tag) as a hack so that IJwB can recognize this is an intellij plugin - java_import( - name = name, - jars = [jar_target_name], - tags = ["intellij-plugin"] + tags, - target_compatible_with = target_compatible_with, testonly = testonly, - **kwargs ) diff --git a/build_defs/intellij_plugin_debug.bzl b/build_defs/intellij_plugin_debug.bzl new file mode 100644 index 00000000000..80e10941cf6 --- /dev/null +++ b/build_defs/intellij_plugin_debug.bzl @@ -0,0 +1,42 @@ +load(":common.bzl", "intellij_common") + +SUFFIX = ".intellij-plugin-debug-target-deploy-info" + +def _create_deploy_location(path, file): + return struct( + execution_path = file.path, + deploy_location = path, + ) + +def _intellij_plugin_debug_target_impl(ctx): + output = ctx.actions.declare_file(ctx.label.name + SUFFIX) + layout = intellij_common.compute_plugin_layout(ctx.attr.prefix, ctx.attr.deps) + + deploy_info = struct( + deploy_files = [_create_deploy_location(path, file) for path, file in layout.items()], + java_agent_deploy_files = [], + ) + + ctx.actions.write(output, proto.encode_text(deploy_info)) + + return [DefaultInfo(files = depset(layout.values() + [output]))] + +intellij_plugin_debug_target = rule( + implementation = _intellij_plugin_debug_target_impl, + doc = """Creates plugin target debuggable from IntelliJ. + + All files in deps are mapped into the plugin sandbox in the same mannar as they would be mapped + into the release zip. + """, + attrs = { + "deps": attr.label_list( + doc = "List of dependencies to deploy to the sandbox.", + allow_files = False, + mandatory = True, + ), + "prefix": attr.string( + doc = "The directory name inside the plugins folder.", + mandatory = True, + ), + }, +) diff --git a/build_defs/intellij_plugin_debug_target.bzl b/build_defs/intellij_plugin_debug_target.bzl deleted file mode 100644 index 6390eae0137..00000000000 --- a/build_defs/intellij_plugin_debug_target.bzl +++ /dev/null @@ -1,127 +0,0 @@ -"""IntelliJ plugin debug target rule used for debugging IntelliJ plugins. - -Creates a plugin target debuggable from IntelliJ. Any files in -the 'deps' and 'javaagents' attribute are deployed to the plugin sandbox. - -Any files are stripped of their prefix and installed into -/plugins. If you need structure, first put the files -into //build_defs:build_defs%repackage_files. - -intellij_plugin_debug_targets can be nested. - -repackaged_files( - name = "foo_files", - srcs = [ - ":my_plugin_jar", - ":my_additional_plugin_files", - ], - prefix = "plugins/foo/lib", -) - -intellij_plugin_debug_target( - name = "my_debug_target", - deps = [ - ":my_jar", - ], - javaagents = [ - ":agent_deploy.jar", - ], -) - -""" - -load("//build_defs:build_defs.bzl", "output_path", "repackaged_files_data") - -SUFFIX = ".intellij-plugin-debug-target-deploy-info" - -_AspectIntellijPluginDeployInfo = provider("", fields = ["input_files", "deploy_info"]) - -_IntellijPluginDeployInfo = provider("", fields = ["deploy_files", "java_agent_deploy_files"]) - -def _repackaged_deploy_file(f, repackaging_data): - return struct( - src = f, - deploy_location = output_path(f, repackaging_data), - ) - -def _flat_deploy_file(f): - return struct( - src = f, - deploy_location = f.basename, - ) - -def _intellij_plugin_debug_target_aspect_impl(target, ctx): - aspect_intellij_plugin_deploy_info = None - - files = target.files - if ctx.rule.kind == "intellij_plugin_debug_target": - aspect_intellij_plugin_deploy_info = target[_IntellijPluginDeployInfo] - elif ctx.rule.kind == "_repackaged_files": - data = target[repackaged_files_data] - aspect_intellij_plugin_deploy_info = struct( - deploy_files = [_repackaged_deploy_file(f, data) for f in data.files.to_list()], - java_agent_deploy_files = [], - ) - - # TODO(brendandouglas): Remove when migrating to Bazel 0.5, when DefaultInfo - # provider can be populated by '_repackaged_files' directly - files = depset(transitive = [files, data.files]) - else: - aspect_intellij_plugin_deploy_info = struct( - deploy_files = [_flat_deploy_file(f) for f in target.files.to_list()], - java_agent_deploy_files = [], - ) - return _AspectIntellijPluginDeployInfo( - input_files = files, - deploy_info = aspect_intellij_plugin_deploy_info, - ) - -_intellij_plugin_debug_target_aspect = aspect( - implementation = _intellij_plugin_debug_target_aspect_impl, -) - -def _build_deploy_info_file(deploy_file): - return struct( - execution_path = deploy_file.src.path, - deploy_location = deploy_file.deploy_location, - ) - -def _intellij_plugin_debug_target_impl(ctx): - files = depset() - deploy_files = [] - java_agent_deploy_files = [] - for target in ctx.attr.deps: - files = depset(transitive = [files, target[_AspectIntellijPluginDeployInfo].input_files]) - deploy_files.extend(target[_AspectIntellijPluginDeployInfo].deploy_info.deploy_files) - java_agent_deploy_files.extend(target[_AspectIntellijPluginDeployInfo].deploy_info.java_agent_deploy_files) - for target in ctx.attr.javaagents: - files = depset(transitive = [files, target[_AspectIntellijPluginDeployInfo].input_files]) - java_agent_deploy_files.extend(target[_AspectIntellijPluginDeployInfo].deploy_info.deploy_files) - java_agent_deploy_files.extend(target[_AspectIntellijPluginDeployInfo].deploy_info.java_agent_deploy_files) - deploy_info = struct( - deploy_files = [_build_deploy_info_file(f) for f in deploy_files], - java_agent_deploy_files = [_build_deploy_info_file(f) for f in java_agent_deploy_files], - ) - output = ctx.actions.declare_file(ctx.label.name + SUFFIX) - ctx.actions.write(output, proto.encode_text(deploy_info)) - - # We've already consumed any dependent intellij_plugin_debug_targets into our own, - # do not build or report these - files = depset([f for f in files.to_list() if not f.path.endswith(SUFFIX)]) - files = depset([output], transitive = [files]) - - return [ - DefaultInfo(files = files), - _IntellijPluginDeployInfo( - deploy_files = deploy_files, - java_agent_deploy_files = java_agent_deploy_files, - ), - ] - -intellij_plugin_debug_target = rule( - implementation = _intellij_plugin_debug_target_impl, - attrs = { - "deps": attr.label_list(aspects = [_intellij_plugin_debug_target_aspect]), - "javaagents": attr.label_list(aspects = [_intellij_plugin_debug_target_aspect]), - }, -) diff --git a/build_defs/intellij_plugin_library.bzl b/build_defs/intellij_plugin_library.bzl index 9167f989ea1..cfea890c69a 100644 --- a/build_defs/intellij_plugin_library.bzl +++ b/build_defs/intellij_plugin_library.bzl @@ -8,6 +8,7 @@ IntellijPluginLibraryInfo = provider( "plugin_xmls": "Depset of files", "optional_plugin_xmls": "Depset of OptionalPluginXmlInfo providers", "java_info": "Single JavaInfo provider (depreacated rules should get JavaInfo directly form the target)", + "runfiles": "Runfiles required by the plugin library and included in zip file. Track this separatly to not get any jars in here.", }, ) @@ -16,7 +17,7 @@ OptionalPluginXmlInfo = provider( fields = ["optional_plugin_xmls"], ) -def _resource_file(target): +def _single_file(target): """Makes sore that every target in the resource mapping privdes a single file.""" files = target[DefaultInfo].files.to_list() @@ -30,12 +31,12 @@ def _import_resources(ctx): output = ctx.actions.declare_file(ctx.label.name + "_resource.jar") mapping = [ - "%s=%s" % (paths.join("resources", path), _resource_file(target).path) + "%s=%s" % (paths.join("resources", path), _single_file(target).path) for path, target in ctx.attr.resources.items() ] ctx.actions.run( - inputs = [_resource_file(target) for target in ctx.attr.resources.values()], + inputs = [_single_file(target) for target in ctx.attr.resources.values()], outputs = [output], mnemonic = "IntellijPluginResource", progress_message = "Creating intellij plugin resource jar for %{label}", @@ -45,6 +46,19 @@ def _import_resources(ctx): return JavaInfo(output_jar = output, compile_jar = output) +def _import_runfiles(ctx): + """Builds a runfile tree form the data mapping.""" + symlink_map = { + path: _single_file(target) + for path, target in ctx.attr.data.items() + } + + return ctx.runfiles(symlinks = symlink_map).merge_all([ + dep[IntellijPluginLibraryInfo].runfiles + for dep in ctx.attr.deps + if IntellijPluginLibraryInfo in dep + ]) + def _merge_plugin_xmls(ctx): """Merges all dependent plugin_xmls and the current one.""" return depset( @@ -79,9 +93,12 @@ def _intellij_plugin_library_rule_impl(ctx): plugin_xmls = _merge_plugin_xmls(ctx), optional_plugin_xmls = _merge_optional_plugin_xmls(ctx), java_info = java_info, + runfiles = _import_runfiles(ctx), ) - return [plugin_info, java_info] + default_info = DefaultInfo(runfiles = plugin_info.runfiles) + + return [plugin_info, java_info, default_info] _intellij_plugin_library = rule( implementation = _intellij_plugin_library_rule_impl, @@ -90,6 +107,10 @@ _intellij_plugin_library = rule( doc = "Maps a file to a specific location inside the resource directory of the plugin.", allow_files = True, ), + "data": attr.string_keyed_label_dict( + doc = "Maps a file to a specific location inside the plugin zip and the runfiles tree.", + allow_files = True, + ), "deps": attr.label_list( doc = "List of java dependencies and plugin dependencies for this library", providers = [[JavaInfo], [JavaInfo, IntellijPluginLibraryInfo]], @@ -123,7 +144,7 @@ def intellij_plugin_library(name, srcs = None, deps = None, **kwargs): kt_jvm_library( name = name + "_ktlib", srcs = srcs, - deps = deps + ["//intellij_platform_sdk:plugin_api",], + deps = deps + ["//intellij_platform_sdk:plugin_api"], visibility = ["//visibility:private"], ) @@ -133,4 +154,4 @@ def intellij_plugin_library(name, srcs = None, deps = None, **kwargs): name = name, deps = deps, **kwargs - ) \ No newline at end of file + ) diff --git a/build_defs/intellij_plugin_zip.bzl b/build_defs/intellij_plugin_zip.bzl new file mode 100644 index 00000000000..422daa4e14c --- /dev/null +++ b/build_defs/intellij_plugin_zip.bzl @@ -0,0 +1,49 @@ +load("@bazel_skylib//lib:paths.bzl", "paths") +load(":common.bzl", "intellij_common") + +def _intellij_plugin_zip_impl(ctx): + output = ctx.actions.declare_file(ctx.attr.filename) + layout = intellij_common.compute_plugin_layout(ctx.attr.prefix, ctx.attr.deps) + + mapping = ["%s=%s" % (path, file.path) for path, file in layout.items()] + + ctx.actions.run( + inputs = layout.values(), + outputs = [output], + mnemonic = "IntellijPluginZip", + progress_message = "Creating intellij plugin zip for %{label}", + executable = ctx.executable._zipper, + arguments = ["c", output.path] + mapping, + ) + + return [DefaultInfo(files = depset([output]))] + +intellij_plugin_zip = rule( + implementation = _intellij_plugin_zip_impl, + doc = """Creates an intellij deployable zip archive. + + All files of the dependencies are included in the `lib` directory and all runfiles are included + from the root of the archive. See `intellij_common.compute_plugin_layout` for details on the + layout. + """, + attrs = { + "deps": attr.label_list( + doc = "List of dependencies to be included in the zip.", + allow_files = False, + mandatory = True, + ), + "prefix": attr.string( + doc = "The prefix inside the zip archive.", + mandatory = True, + ), + "filename": attr.string( + doc = "The name of the zip archive.", + mandatory = True, + ), + "_zipper": attr.label( + default = Label("@bazel_tools//tools/zip:zipper"), + executable = True, + cfg = "exec", + ), + }, +) diff --git a/build_defs/restrictions.bzl b/build_defs/restrictions.bzl deleted file mode 100644 index bad43f9db75..00000000000 --- a/build_defs/restrictions.bzl +++ /dev/null @@ -1,255 +0,0 @@ -""" -Restriction rules for plugin development - -This file implements an aspect that restricts the -transitive dependencies of intellij_plugins that -decide to do so. - -This prevents large transitive google3 dependencies -from making it into the plugin that runs on -a different context (IntelliJ and not google3) -""" - -# BEGIN-REPO -load("@bazel_skylib//rules:build_test.bzl", "build_test") -# END-REPO - -# intellij_plugin will validate that all dependencies from these pacakages are self contained -_project = [ -] - -_tests = [ -] - -# Targets from the project scope that should be reported as external targets. -_not_project_for_tests = [ -] - -# A set of external dependencies that can be built outside of google3 -_valid = [ -] - -EXTERNAL_DEPENDENCIES = { -} - -# List of targets that use internal only Guava APIs that need to be cleaned up. -# Targets in this list are java_library's that do not have the line: -# plugins = ["//java/com/google/devtools/build/buildjar/plugin/annotations:google_internal_checker"], -EXISTING_UNCHECKED = [ -] - -# A temporary list of external targets that plugins are depending on. DO NOT ADD TO THIS -ALLOWED_EXTERNAL_TEST_DEPENDENCIES = [ -] - -# A list of targets currently with not allowed dependencies -EXISTING_EXTERNAL_TEST_VIOLATIONS = [ -] - -RestrictedInfo = provider( - doc = "The dependencies, per target, outside the project", - fields = { - "roots": "A depset of roots of all target trees that don't have external dependencies", - "dependencies": "A map from target to external dependencies", - "unchecked": "A list of targets that are still unchecked for guava internal APIs", - }, -) - -def _in_set(target, set): - pkg = target.label.package - for p in set: - if pkg == p or pkg.startswith(p + "/"): - return p - if str(target.label) == p: - return p - - return None - -def _in_project(target): - return _in_set(target, _project) - -def _in_tests(target): - lbl = str(target.label) - return (lbl.endswith("_test") or lbl.endswith("_tests") or lbl.endswith(":tests")) and _in_project(target) or _in_set(target, _tests) - -def _get_deps(ctx): - deps = [] - if hasattr(ctx.rule.attr, "deps"): - deps.extend(ctx.rule.attr.deps) - if hasattr(ctx.rule.attr, "exports"): - deps.extend(ctx.rule.attr.exports) - if hasattr(ctx.rule.attr, "runtime_deps"): - deps.extend(ctx.rule.attr.runtime_deps) - if hasattr(ctx.rule.attr, "data"): - deps.extend(ctx.rule.attr.data) - if hasattr(ctx.rule.attr, "tests"): - deps.extend(ctx.rule.attr.tests) - return deps - -def _restricted_deps_aspect_impl(target, ctx): - if not _in_project(target): - return [] - - unchecked = [] - if ctx.rule.kind == "java_library": - if ctx.rule.attr.plugins: - labels = [t.label for t in ctx.rule.attr.plugins] - if (Label("//java/com/google/devtools/build/buildjar/plugin/annotations:google_internal_checker") not in labels): - unchecked.append(target) - else: - unchecked.append(target) - - nested_roots = [] - dependencies = {} - nested_unchecked = [] - outside_project = [] - for d in _get_deps(ctx): - if not _in_project(d) and not _in_set(d, _valid): - outside_project.append(d) - if RestrictedInfo in d: - dependencies.update(d[RestrictedInfo].dependencies) - nested_unchecked.append(d[RestrictedInfo].unchecked) - nested_roots.append(d[RestrictedInfo].roots) - - if outside_project: - dependencies[target] = outside_project - - if dependencies: - # This target cannot be a root as either itself or its dependencies depend on out of project targets - roots = depset(direct = [], transitive = nested_roots) - else: - # No external dependencies on the entire subtree, we are a root - roots = depset(direct = [target]) - - return [RestrictedInfo( - dependencies = dependencies, - unchecked = depset(direct = unchecked, transitive = nested_unchecked), - roots = roots, - )] - -# buildifier: disable=function-docstring -def validate_unchecked_internal(unchecked): - not_allowed_to_be_unchecked = [t for t in unchecked if t not in EXISTING_UNCHECKED] - checked_still_in_list = [t for t in EXISTING_UNCHECKED if t not in unchecked] - error = "" - if not_allowed_to_be_unchecked: - error += "The following targets do not have either google_internal_checker or beta_checker on:\n " + "\n ".join(not_allowed_to_be_unchecked) + "\n" - if checked_still_in_list: - error += "The following targets are checked but still in the EXISTING_UNCHECKED list:\n " + "\n ".join(checked_still_in_list) + "\n" - if error: - fail(error) - -def _restricted_test_deps_aspect_impl(target, ctx): - if not _in_tests(target): - return [] - - dependencies = {} - outside_project = [] - for d in _get_deps(ctx): - if not _in_tests(d) and not _in_set(d, _valid) and (not _in_project(d) or _in_set(d, _not_project_for_tests)): - outside_project.append(d) - if RestrictedInfo in d: - dependencies.update(d[RestrictedInfo].dependencies) - - if outside_project: - dependencies[target] = outside_project - - return [ - RestrictedInfo(dependencies = dependencies), - ] - -def validate_restrictions(dependencies): - external_dependencies = {str(k.label): [str(vt.label) for vt in v] for (k, v) in dependencies.items()} - if external_dependencies != EXTERNAL_DEPENDENCIES: - error = ( - ) - fail(error) - -# buildifier: disable=function-docstring -def _validate_test_restrictions(dependencies, allowed_external, existing_violations): - violations = sorted([str(d.label) for d in dependencies.keys()]) - error = "" - if violations != sorted(existing_violations): - new_violations = [t for t in violations if t not in existing_violations] - no_longer_violations = [t for t in existing_violations if t not in violations] - if new_violations: - error += ( - "These targets now depend on external targets:\n " + - "\n ".join( - [ - str(t.label) + " =>\n " + - "\n ".join([str(vt.label) for vt in v]) - for (t, v) in dependencies.items() - if str(t.label) in new_violations - ], - ) + "\n" - ) - - if no_longer_violations: - error += "The following targets no longer depend on external targets, please remove from restrictions.bzl:\n " + "\n ".join(no_longer_violations) + "\n" - - for target, outside_project in dependencies.items(): - invalid = [dep for dep in outside_project if not _in_set(dep, allowed_external)] - if invalid: - tgts = [str(t.label) for t in invalid] - error += "Invalid dependencies for target " + str(target.label) + "\n " + "\n ".join(tgts) + "\n" - if error != "": - error += "For more information see restrictions.bzl" - fail(error) - - # Check allowed_external does not contain unnecessary targets - current_allowed_external = {} - for target, outside_project in dependencies.items(): - for out in outside_project: - item = _in_set(out, allowed_external) - if item: - current_allowed_external[item] = item - if sorted(current_allowed_external.keys()) != sorted(allowed_external): - no_longer_needed = [e for e in allowed_external if e not in current_allowed_external] - if no_longer_needed: - tgts = [str(t) for t in no_longer_needed] - fail("The following external dependencies are no longer needed: " + "\n " + "\n ".join(tgts) + "\n") - -restricted_deps_aspect = aspect( - implementation = _restricted_deps_aspect_impl, - attr_aspects = ["*"], -) - -restricted_test_deps_aspect = aspect( - implementation = _restricted_test_deps_aspect_impl, - attr_aspects = ["*"], -) - -def _validate_test_dependencies_impl(ctx): - dependencies = {} - for k in ctx.attr.deps: - if RestrictedInfo in k: - dependencies.update(k[RestrictedInfo].dependencies) - _validate_test_restrictions(dependencies, ctx.attr.allowed_external_dependencies, ctx.attr.existing_external_violations) - return [DefaultInfo(files = depset())] - -_validate_test_dependencies = rule( - implementation = _validate_test_dependencies_impl, - attrs = { - "allowed_external_dependencies": attr.string_list(), - "existing_external_violations": attr.string_list(), - "deps": attr.label_list(aspects = [restricted_test_deps_aspect]), - "data": attr.label(), - }, -) - -# BEGIN-REPO -def validate_test_dependencies(name, deps, **kwargs): - _validate_test_dependencies( - name = name + "_check", - deps = deps, - testonly = 1, - **kwargs - ) - build_test( - name = name, - testonly = 1, - targets = deps + [":" + name + "_check"], - ) - -# END-REPO diff --git a/clwb/BUILD b/clwb/BUILD index 15c3f031aa1..b47e240b71a 100644 --- a/clwb/BUILD +++ b/clwb/BUILD @@ -12,16 +12,15 @@ load("//:version.bzl", "VERSION") load( "//build_defs:build_defs.bzl", "intellij_plugin", - "intellij_plugin_library", "optional_plugin_xml", - "plugin_deploy_zip", - "repackaged_files", "stamped_plugin_xml", ) load( - "//build_defs:intellij_plugin_debug_target.bzl", + "//build_defs:intellij_plugin_debug.bzl", "intellij_plugin_debug_target", ) +load("//build_defs:intellij_plugin_library.bzl", "intellij_plugin_library") +load("//build_defs:intellij_plugin_zip.bzl", "intellij_plugin_zip") load("//intellij_platform_sdk:build_defs.bzl", "select_for_version") load( "//testing:test_defs.bzl", @@ -33,6 +32,7 @@ licenses(["notice"]) intellij_plugin_library( name = "plugin_library", + data = {"gdb/gdbserver": ":gdbserver"}, optional_plugin_xmls = ["optional_clwb_oclang"], plugin_xmls = ["src/META-INF/clwb.xml"], visibility = PLUGIN_PACKAGES_VISIBILITY, @@ -73,6 +73,7 @@ kt_jvm_library( "//base/src/com/google/idea/blaze/base/command/buildresult/bepparser", "//common/actions", "//common/experiments", + "//common/util:datafiles", "//cpp", "//intellij_platform_sdk:jsr305", "//intellij_platform_sdk:plugin_api", @@ -100,34 +101,17 @@ intellij_plugin( ], ) -repackaged_files( - name = "plugin_jar", - srcs = [":clwb_bazel"], - prefix = "clwb/lib", -) - -repackaged_files( - name = "clwb_bazel_gdbserver_wrapper", - srcs = ["//clwb:gdbserver_wrapper"], - prefix = "clwb/gdb", - strip_prefix = "/clwb", -) - intellij_plugin_debug_target( name = "clwb_bazel_dev", - deps = [ - ":clwb_bazel_gdbserver_wrapper", - ":plugin_jar", - ], + prefix = "clwb", + deps = [":clwb_bazel"], ) -plugin_deploy_zip( +intellij_plugin_zip( name = "clwb_bazel_zip", - srcs = [ - ":clwb_bazel_gdbserver_wrapper", - ":plugin_jar", - ], - zip_filename = "clwb_bazel.zip", + filename = "clwb_bazel.zip", + prefix = "clwb", + deps = [":clwb_bazel"], ) intellij_unit_test_suite( @@ -144,12 +128,6 @@ intellij_unit_test_suite( ], ) -filegroup( - name = "gdbserver_wrapper", - srcs = ["gdbserver"], - visibility = ["//visibility:public"], -) - clwb_headless_test( name = "simple_headless_test", srcs = ["tests/headlesstests/com/google/idea/blaze/clwb/SimpleTest.java"], diff --git a/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt b/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt index edc36508ad1..4259c7c8514 100644 --- a/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt +++ b/clwb/src/com/google/idea/blaze/clwb/run/BlazeGDBServerProvider.kt @@ -21,6 +21,7 @@ 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.google.idea.common.util.Datafiles import com.google.idea.sdkcompat.clion.debug.CidrDebuggerPathManagerAdapter import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.util.registry.Registry @@ -36,8 +37,6 @@ 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 { @@ -46,14 +45,7 @@ object BlazeGDBServerProvider { * 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() - } - } + private val GDBSERVER_WRAPPER: Path by Datafiles.resolveLazy("gdb/gdbserver") // These flags are used when debugging cc_binary targets when remote debugging // is enabled (cc.remote.debugging) diff --git a/clwb/test_defs.bzl b/clwb/test_defs.bzl index acb1815dea5..b534514eb68 100644 --- a/clwb/test_defs.bzl +++ b/clwb/test_defs.bzl @@ -20,11 +20,9 @@ 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", + "//clwb:plugin_library", "//base", "//shared", "//common/util:process", @@ -36,9 +34,6 @@ 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/common/util/BUILD b/common/util/BUILD index b03a5a3dc45..cfb780428a4 100644 --- a/common/util/BUILD +++ b/common/util/BUILD @@ -1,11 +1,8 @@ load("@rules_java//java:defs.bzl", "java_library", "java_test") -load( - "//:build-visibility.bzl", - "COMMON_PLUGINS_VISIBILITY", -) +load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") load("//intellij_platform_sdk:build_defs.bzl", "select_for_channel") -package(default_visibility = COMMON_PLUGINS_VISIBILITY) +package(default_visibility = ["//visibility:public"]) licenses(["notice"]) @@ -47,6 +44,12 @@ java_library( ], ) +kt_jvm_library( + name = "datafiles", + srcs = ["src/com/google/idea/common/util/Datafiles.kt"], + deps = ["//intellij_platform_sdk:plugin_api"], +) + java_test( name = "CommandLineTaskTest", size = "medium", diff --git a/common/util/src/com/google/idea/common/util/Datafiles.kt b/common/util/src/com/google/idea/common/util/Datafiles.kt new file mode 100644 index 00000000000..c0c4a467ec8 --- /dev/null +++ b/common/util/src/com/google/idea/common/util/Datafiles.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2025 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.common.util + +import com.intellij.util.PathUtil +import java.nio.file.Path + +/** + * Utility object for resolving data files, i.e. files list in the data map of the + * intellij_plugin_library rule. + */ +object Datafiles { + + private val TEST_SRCDIR by lazy { System.getenv("TEST_SRCDIR") } + + private val TEST_WORKSPACE by lazy { System.getenv("TEST_WORKSPACE") } + + /** + * Load data files from runfiles during testing. + */ + val fromRunfiles get() = TEST_SRCDIR != null && TEST_WORKSPACE != null + + /** + * Load data files from the plugin directory in production. + */ + val fromPluginDir get() = !fromRunfiles + + /** + * Resolve a data file path relative to the correct location for the current environment. + */ + fun resolve(relativePath: String): Path { + return if (fromRunfiles) { + Path.of(TEST_SRCDIR, TEST_WORKSPACE, relativePath) + } else { + val jarPath = Path.of(PathUtil.getJarPathForClass(Datafiles::class.java)) + // go two up from the plugin jar to find the plugin's root directory + jarPath.parent.parent.resolve(relativePath) + } + } + + fun resolveLazy(relativePath: String): Lazy { + return lazy { resolve(relativePath) } + } +} \ No newline at end of file