diff --git a/.idea/modules.xml b/.idea/modules.xml
index a1e93f5c5ad6..6ec9d4dd4026 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -636,6 +636,7 @@
+
diff --git a/build/bazel-generated-file-list.txt b/build/bazel-generated-file-list.txt
index 066450342957..5399e595ff0a 100644
--- a/build/bazel-generated-file-list.txt
+++ b/build/bazel-generated-file-list.txt
@@ -347,6 +347,7 @@ libraries/detekt-compose-rules
libraries/dokka
libraries/grpc
libraries/grpc-netty-shaded
+libraries/guava
libraries/hamcrest
libraries/hamcrest-more-matchers
libraries/io
diff --git a/libraries/README.md b/libraries/README.md
new file mode 100644
index 000000000000..e010f094c2ed
--- /dev/null
+++ b/libraries/README.md
@@ -0,0 +1,190 @@
+# Product Embedded Library Modules
+
+This directory contains product-embedded library modules that make third-party libraries available throughout the IntelliJ platform.
+
+## Creating a New Library Module
+
+To create a new product-embedded module for a library (e.g., for a library named "example-lib"):
+
+### 1. Create Module Structure
+
+Create the following directory structure:
+```
+community/libraries/example-lib/
+├── intellij.libraries.example.lib.iml
+└── resources/
+ └── intellij.libraries.example.lib.xml
+```
+
+### 2. Create Module Definition File (`.iml`)
+
+File: `intellij.libraries.example.lib.iml`
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+**Key points:**
+- The `name` attribute in `` must match the library name defined in `.idea/libraries/ExampleLibrary.xml`
+- If the module depends on other embedded modules, add them as additional `` entries (see `junit4` example)
+
+### 3. Create Plugin Descriptor
+
+File: `resources/intellij.libraries.example.lib.xml`
+
+```xml
+
+
+```
+
+**Note:** If your module depends on other library modules, add them:
+```xml
+
+
+
+
+
+```
+
+### 4. Register in Essential Libraries (if needed)
+
+If the library should be loaded as an essential embedded module, add it to:
+
+File: `community/platform/platform-resources/src/META-INF/essential-libs.xml`
+
+```xml
+
+```
+
+Add the entry in alphabetical order.
+
+### 5. Register in Project Modules
+
+Add the module to both `modules.xml` files in alphabetical order:
+
+#### Root project file: `.idea/modules.xml`
+```xml
+
+```
+
+#### Community project file: `community/.idea/modules.xml`
+```xml
+
+```
+
+**Important:**
+- Root `modules.xml` uses full path with `community/` prefix
+- Community `modules.xml` uses relative path without `community/` prefix
+
+### 6. Build File
+
+**Do NOT create `BUILD.bazel` manually** - it will be auto-generated by the build system.
+
+### 7. Verify Changes
+
+After creating the module, run the following tests to ensure project structure consistency:
+
+- **`IdeaUltimatePluginStructureTest`** - Verifies the plugin structure for IDEA Ultimate
+- **`CommunityProjectConsistencyTest`** - Verifies community project consistency
+
+Run tests using:
+```bash
+./tests.cmd -Dintellij.build.test.patterns=IdeaUltimatePluginStructureTest
+./tests.cmd -Dintellij.build.test.patterns=CommunityProjectConsistencyTest
+```
+
+## Examples
+
+- **Simple library:** `hamcrest/` - single library dependency
+- **Library with dependencies:** `junit4/` - depends on hamcrest module
+- **Multiple libraries:** `grpc/` - exports multiple related libraries
+
+## Naming Conventions
+
+- **Directory name:** Use kebab-case (e.g., `example-lib`, `grpc-netty-shaded`)
+- **Module name:** Use dot-separated format (e.g., `intellij.libraries.example.lib`, `intellij.libraries.grpc.netty.shaded`)
+- **IML filename:** Should match the module name (e.g., `intellij.libraries.example.lib.iml`)
+- **Plugin XML filename:** Should match the module name (e.g., `intellij.libraries.example.lib.xml`)
+
+## Prerequisites
+
+Before creating a library module, ensure:
+1. The library is already defined in `.idea/libraries/` as a project library
+2. The library artifacts are available in the Maven repository or bundled appropriately
+
+## Migrating Existing Library References to Library Modules
+
+When converting a project-level library to an embedded library module, you need to update all `.iml` files that reference the library.
+
+### Automated Migration Script
+
+A generic Node.js script is available to automate the migration of library references across the entire project:
+
+**Location:** `community/libraries/replace-lib-with-module.js`
+
+**Usage:**
+```bash
+# Dry run to preview changes for Guava
+node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
+
+# Apply Guava replacement
+node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
+
+# Replace a different library
+node community/libraries/replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson
+
+# Add custom exclude directories
+node community/libraries/replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --exclude=custom-dir
+
+# Show help
+node community/libraries/replace-lib-with-module.js --help
+```
+
+**Script Features:**
+- Parses `.idea/modules.xml` to find all IML files
+- Supports multiple replacement patterns:
+ - Regular scope: `` → ``
+ - TEST scope: `` → ``
+ - TEST scope with exported attribute
+- Excludes specified directories (default: `fleet/`, `toolbox/`)
+- Provides detailed statistics of files modified and replacements made
+- Dry-run mode to preview changes without modifying files
+
+**Example Output:**
+```
+Configuration:
+ Old library: Guava
+ New module: intellij.libraries.guava
+ Excluded dirs: fleet, toolbox
+ Dry run: false
+
+Found 15000 module files in modules.xml
+
+Processing IML files:
+ ✓ platform/ssh/intellij.platform.ssh.iml (1 regular)
+ ✓ community/android/testartifacts/intellij.android.testartifacts.iml (1 regular)
+ ...
+
+Summary:
+Files scanned: 14500
+Files modified: 450
+Files skipped: 550
+Replacements by type:
+ Regular scope: 396
+ TEST scope: 109
+ TEST scope (exported): 3
+ Total: 508
+```
diff --git a/libraries/guava/BUILD.bazel b/libraries/guava/BUILD.bazel
new file mode 100644
index 000000000000..02a3c25f77ba
--- /dev/null
+++ b/libraries/guava/BUILD.bazel
@@ -0,0 +1,18 @@
+### auto-generated section `build intellij.libraries.guava` start
+load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
+
+resourcegroup(
+ name = "guava_resources",
+ srcs = glob(["resources/**/*"]),
+ strip_prefix = "resources"
+)
+
+jvm_library(
+ name = "guava",
+ visibility = ["//visibility:public"],
+ srcs = glob([], allow_empty = True),
+ resources = [":guava_resources"],
+ exports = ["@lib//:guava"],
+ runtime_deps = ["@lib//:guava"]
+)
+### auto-generated section `build intellij.libraries.guava` end
\ No newline at end of file
diff --git a/libraries/guava/intellij.libraries.guava.iml b/libraries/guava/intellij.libraries.guava.iml
new file mode 100644
index 000000000000..c76176264e6b
--- /dev/null
+++ b/libraries/guava/intellij.libraries.guava.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libraries/guava/resources/intellij.libraries.guava.xml b/libraries/guava/resources/intellij.libraries.guava.xml
new file mode 100644
index 000000000000..6fb76a66265d
--- /dev/null
+++ b/libraries/guava/resources/intellij.libraries.guava.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/libraries/replace-lib-with-module.js b/libraries/replace-lib-with-module.js
new file mode 100644
index 000000000000..b1601902814c
--- /dev/null
+++ b/libraries/replace-lib-with-module.js
@@ -0,0 +1,267 @@
+#!/usr/bin/env node
+
+/**
+ * Generic script to replace library references with module references in IntelliJ project IML files.
+ *
+ * Usage:
+ * node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]
+ *
+ * Options:
+ * --old-lib=NAME Library name to replace (required)
+ * --new-module=NAME Target module name (required)
+ * --dry-run Show what would be changed without modifying files
+ * --exclude=DIR Additional directory to exclude (can be used multiple times, default: fleet, toolbox)
+ * --help Show this help message
+ *
+ * Examples:
+ * # Dry run for Guava
+ * node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
+ *
+ * # Apply Guava replacement
+ * node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
+ *
+ * # Replace another library with custom exclusions
+ * node replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson --exclude=custom-dir
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+// Parse command line arguments
+const args = process.argv.slice(2);
+const config = {
+ dryRun: args.includes('--dry-run'),
+ oldLib: null,
+ newModule: null,
+ excludeDirs: ['fleet', 'toolbox'],
+ showHelp: args.includes('--help')
+};
+
+// Parse custom arguments
+for (const arg of args) {
+ if (arg.startsWith('--old-lib=')) {
+ config.oldLib = arg.split('=')[1];
+ } else if (arg.startsWith('--new-module=')) {
+ config.newModule = arg.split('=')[1];
+ } else if (arg.startsWith('--exclude=')) {
+ const dir = arg.split('=')[1];
+ if (!config.excludeDirs.includes(dir)) {
+ config.excludeDirs.push(dir);
+ }
+ }
+}
+
+if (config.showHelp) {
+ console.log(`
+Generic script to replace library references with module references in IntelliJ project IML files.
+
+Usage:
+ node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]
+
+Options:
+ --old-lib=NAME Library name to replace (required)
+ --new-module=NAME Target module name (required)
+ --dry-run Show what would be changed without modifying files
+ --exclude=DIR Additional directory to exclude (can be used multiple times, default: fleet, toolbox)
+ --help Show this help message
+
+Examples:
+ # Dry run for Guava
+ node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava --dry-run
+
+ # Apply Guava replacement
+ node replace-lib-with-module.js --old-lib=Guava --new-module=intellij.libraries.guava
+
+ # Replace another library with custom exclusions
+ node replace-lib-with-module.js --old-lib=Jackson --new-module=intellij.libraries.jackson --exclude=custom-dir
+`);
+ process.exit(0);
+}
+
+// Validate required arguments
+if (!config.oldLib || !config.newModule) {
+ console.error('Error: Both --old-lib and --new-module are required arguments.\n');
+ console.log('Usage: node replace-lib-with-module.js --old-lib=LibName --new-module=intellij.libraries.libname [options]');
+ console.log('Run with --help for more information.');
+ process.exit(1);
+}
+
+const PROJECT_ROOT = path.join(__dirname, '..', '..');
+const MODULES_XML = path.join(PROJECT_ROOT, '.idea', 'modules.xml');
+
+console.log('Configuration:');
+console.log(` Old library: ${config.oldLib}`);
+console.log(` New module: ${config.newModule}`);
+console.log(` Excluded dirs: ${config.excludeDirs.join(', ')}`);
+console.log(` Dry run: ${config.dryRun}`);
+console.log('');
+
+// Statistics
+const stats = {
+ filesScanned: 0,
+ filesModified: 0,
+ filesSkipped: 0,
+ replacements: {
+ regular: 0,
+ testScope: 0,
+ testScopeExported: 0
+ }
+};
+
+/**
+ * Extract IML file paths from modules.xml
+ */
+function extractImlPaths(modulesXmlPath) {
+ const content = fs.readFileSync(modulesXmlPath, 'utf-8');
+ const filepathRegex = /filepath="\$PROJECT_DIR\$\/([^"]+)"/g;
+ const paths = [];
+ let match;
+
+ while ((match = filepathRegex.exec(content)) !== null) {
+ paths.push(match[1]);
+ }
+
+ return paths;
+}
+
+/**
+ * Check if path should be excluded
+ */
+function shouldExclude(filePath) {
+ for (const excludeDir of config.excludeDirs) {
+ if (filePath.startsWith(excludeDir + '/') || filePath.includes('/' + excludeDir + '/')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Create replacement patterns based on library and module names
+ */
+function createReplacementPatterns(oldLib, newModule) {
+ return [
+ {
+ name: 'regular',
+ //
+ pattern: new RegExp(
+ ``,
+ 'g'
+ ),
+ replacement: ``
+ },
+ {
+ name: 'testScope',
+ //
+ pattern: new RegExp(
+ ``,
+ 'g'
+ ),
+ replacement: ``
+ },
+ {
+ name: 'testScopeExported',
+ //
+ pattern: new RegExp(
+ ``,
+ 'g'
+ ),
+ replacement: ``
+ }
+ ];
+}
+
+/**
+ * Process a single IML file
+ */
+function processImlFile(filePath) {
+ const absolutePath = path.join(PROJECT_ROOT, filePath);
+
+ if (!fs.existsSync(absolutePath)) {
+ console.log(` ⚠ File not found: ${filePath}`);
+ stats.filesSkipped++;
+ return;
+ }
+
+ let content = fs.readFileSync(absolutePath, 'utf-8');
+ let modified = false;
+ const fileReplacements = {};
+
+ const patterns = createReplacementPatterns(config.oldLib, config.newModule);
+
+ for (const { name, pattern, replacement } of patterns) {
+ const matches = content.match(pattern);
+ if (matches && matches.length > 0) {
+ fileReplacements[name] = matches.length;
+ stats.replacements[name] += matches.length;
+ content = content.replace(pattern, replacement);
+ modified = true;
+ }
+ }
+
+ stats.filesScanned++;
+
+ if (modified) {
+ stats.filesModified++;
+
+ const replacementSummary = Object.entries(fileReplacements)
+ .map(([type, count]) => `${count} ${type}`)
+ .join(', ');
+
+ console.log(` ✓ ${filePath} (${replacementSummary})`);
+
+ if (!config.dryRun) {
+ fs.writeFileSync(absolutePath, content, 'utf-8');
+ }
+ }
+}
+
+/**
+ * Main execution
+ */
+function main() {
+ console.log('Reading modules from .idea/modules.xml...\n');
+
+ const imlPaths = extractImlPaths(MODULES_XML);
+ console.log(`Found ${imlPaths.length} module files in modules.xml\n`);
+
+ console.log('Processing IML files:\n');
+
+ for (const imlPath of imlPaths) {
+ if (shouldExclude(imlPath)) {
+ stats.filesSkipped++;
+ continue;
+ }
+
+ processImlFile(imlPath);
+ }
+
+ // Print summary
+ console.log('\n' + '='.repeat(60));
+ console.log('Summary:');
+ console.log('='.repeat(60));
+ console.log(`Files scanned: ${stats.filesScanned}`);
+ console.log(`Files modified: ${stats.filesModified}`);
+ console.log(`Files skipped: ${stats.filesSkipped}`);
+ console.log('');
+ console.log('Replacements by type:');
+ console.log(` Regular scope: ${stats.replacements.regular}`);
+ console.log(` TEST scope: ${stats.replacements.testScope}`);
+ console.log(` TEST scope (exported): ${stats.replacements.testScopeExported}`);
+ console.log(` Total: ${stats.replacements.regular + stats.replacements.testScope + stats.replacements.testScopeExported}`);
+
+ if (config.dryRun) {
+ console.log('\n⚠ DRY RUN MODE - No files were modified');
+ console.log('Run without --dry-run to apply changes');
+ } else {
+ console.log('\n✓ All changes applied successfully');
+ }
+}
+
+// Run the script
+try {
+ main();
+} catch (error) {
+ console.error('Error:', error.message);
+ process.exit(1);
+}
diff --git a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt
index f67b2497d895..1b650ab3263a 100644
--- a/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt
+++ b/platform/build-scripts/src/org/jetbrains/intellij/build/impl/JarPackager.kt
@@ -117,8 +117,6 @@ private val predefinedMergeRules = listOf it.startsWith("org.codehaus.groovy:") },
"jsch-agent.jar" to { it, _ -> it.startsWith("jsch-agent") },
rdJarName to { it, _ -> it.startsWith("rd-") },
- // separate file to use in Gradle Daemon classpath
- "guava.jar" to { it, _ -> it == "Guava" },
"opentelemetry.jar" to { it, _ -> it == "opentelemetry" || it == "opentelemetry-semconv" || it.startsWith("opentelemetry-exporter-otlp") },
"bouncy-castle.jar" to { it, _ -> it.startsWith("bouncy-castle-") },
PRODUCT_BACKEND_JAR to { name, filter -> (name.startsWith("License") || name.startsWith("jetbrains.codeWithMe.lobby.server.")) && filter.isBackendProjectLibrary(name) },
diff --git a/platform/platform-resources/src/META-INF/essential-libs.xml b/platform/platform-resources/src/META-INF/essential-libs.xml
index ee76cfbe7dc0..540a1e7ced8b 100644
--- a/platform/platform-resources/src/META-INF/essential-libs.xml
+++ b/platform/platform-resources/src/META-INF/essential-libs.xml
@@ -5,5 +5,7 @@
+
+
\ No newline at end of file