Merge "Remove unit tests jar from suites"
diff --git a/core/Makefile b/core/Makefile
index d85bd76..30a41d1 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -40,6 +40,10 @@
 )
 endef
 
+# Phony target to check PRODUCT_COPY_FILES copy pairs don't contain ELF files
+.PHONY: check-elf-prebuilt-product-copy-files
+check-elf-prebuilt-product-copy-files:
+
 check_elf_prebuilt_product_copy_files := true
 ifneq (,$(filter true,$(BUILD_BROKEN_ELF_PREBUILT_PRODUCT_COPY_FILES)))
 check_elf_prebuilt_product_copy_files :=
@@ -1666,16 +1670,15 @@
 INSTALLED_FILES_FILE_RECOVERY := $(PRODUCT_OUT)/installed-files-recovery.txt
 INSTALLED_FILES_JSON_RECOVERY := $(INSTALLED_FILES_FILE_RECOVERY:.txt=.json)
 
+ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)
+endif
+
 # TODO(b/30414428): Can't depend on INTERNAL_RECOVERYIMAGE_FILES alone like other
 # INSTALLED_FILES_FILE_* rules. Because currently there're cp/rsync/rm commands in
 # build-recoveryimage-target, which would touch the files under TARGET_RECOVERY_OUT and race with
 # the call to FILELIST.
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
-INSTALLED_BOOTIMAGE_TARGET := $(BUILT_BOOTIMAGE_TARGET)
-$(INSTALLED_FILES_FILE_RECOVERY): $(INSTALLED_BOOTIMAGE_TARGET)
-else
-$(INSTALLED_FILES_FILE_RECOVERY): $(INSTALLED_RECOVERYIMAGE_TARGET)
-endif
+$(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERY_RAMDISK_FILES_TIMESTAMP)
 
 $(INSTALLED_FILES_FILE_RECOVERY): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_RECOVERY)
 $(INSTALLED_FILES_FILE_RECOVERY): $(INTERNAL_RECOVERYIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)
@@ -2467,6 +2470,15 @@
 # -----------------------------------------------------------------
 ifdef BUILDING_SYSTEM_IMAGE
 
+# Install system linker configuration
+# Collect all available stub libraries installed in system and install with predefined linker configuration
+SYSTEM_LINKER_CONFIG := $(TARGET_OUT)/etc/linker.config.pb
+$(SYSTEM_LINKER_CONFIG) : $(INTERNAL_SYSTEMIMAGE_FILES) $(LINKER_CONFIG_PATH_system_linker_config) | conv_linker_config
+	$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $(LINKER_CONFIG_PATH_system_linker_config)\
+	  --output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT)"
+
+FULL_SYSTEMIMAGE_DEPS += $(SYSTEM_LINKER_CONFIG)
+
 # installed file list
 # Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.
 # We put installed-files.txt ahead of image itself in the dependency graph
@@ -3951,6 +3963,7 @@
   fec \
   fs_config \
   generate_verity_key \
+  host_init_verifier \
   img2simg \
   img_from_target_files \
   imgdiff \
diff --git a/core/definitions.mk b/core/definitions.mk
index 8219f82..0230340 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2493,15 +2493,25 @@
 # $(2): destination file
 # $(3): message to print on error
 define copy-non-elf-file-checked
-$(2): $(1) $(LLVM_READOBJ)
-	@echo "Copy non-ELF: $$@"
+$(eval check_non_elf_file_timestamp := \
+    $(call intermediates-dir-for,FAKE,check-non-elf-file-timestamps)/$(2).timestamp)
+$(check_non_elf_file_timestamp): $(1) $(LLVM_READOBJ)
+	@echo "Check non-ELF: $$<"
+	$(hide) mkdir -p "$$(dir $$@)"
+	$(hide) rm -f "$$@"
 	$(hide) \
-	    if $(LLVM_READOBJ) -h $$< >/dev/null 2>&1; then \
-	        $(call echo-error,$$@,$(3)); \
-	        $(call echo-error,$$@,found ELF file: $$<); \
+	    if $(LLVM_READOBJ) -h "$$<" >/dev/null 2>&1; then \
+	        $(call echo-error,$(2),$(3)); \
+	        $(call echo-error,$(2),found ELF file: $$<); \
 	        false; \
 	    fi
+	$(hide) touch "$$@"
+
+$(2): $(1) $(check_non_elf_file_timestamp)
+	@echo "Copy non-ELF: $$@"
 	$$(copy-file-to-target)
+
+check-elf-prebuilt-product-copy-files: $(check_non_elf_file_timestamp)
 endef
 
 # The -t option to acp and the -p option to cp is
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 06b5555..250efb2 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -210,6 +210,9 @@
 $(call add_json_bool, EnforceProductPartitionInterface,  $(PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE))
 $(call add_json_str,  DeviceCurrentApiLevelForVendorModules,  $(BOARD_CURRENT_API_LEVEL_FOR_VENDOR_MODULES))
 
+$(call add_json_bool, EnforceInterPartitionJavaSdkLibrary, $(PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY))
+$(call add_json_list, InterPartitionJavaLibraryAllowList, $(PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST))
+
 $(call add_json_bool, InstallExtraFlattenedApexes, $(PRODUCT_INSTALL_EXTRA_FLATTENED_APEXES))
 
 $(call add_json_bool, BoardUsesRecoveryAsBoot, $(BOARD_USES_RECOVERY_AS_BOOT))
diff --git a/core/tasks/mts.mk b/core/tasks/mts.mk
index e800505..e084856 100644
--- a/core/tasks/mts.mk
+++ b/core/tasks/mts.mk
@@ -13,13 +13,20 @@
 # limitations under the License.
 
 ifneq ($(wildcard test/mts/README.md),)
-test_suite_name := mts
-test_suite_tradefed := mts-tradefed
-test_suite_readme := test/mts/README.md
 
-include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk
+mts_test_suites :=
+mts_test_suites += mts
 
-.PHONY: mts
-mts: $(compatibility_zip)
-$(call dist-for-goals, mts, $(compatibility_zip))
+$(foreach module, $(mts_modules), $(eval mts_test_suites += mts-$(module)))
+
+$(foreach suite, $(mts_test_suites), \
+	$(eval test_suite_name := $(suite)) \
+	$(eval test_suite_tradefed := mts-tradefed) \
+	$(eval test_suite_readme := test/mts/README.md) \
+	$(eval include $(BUILD_SYSTEM)/tasks/tools/compatibility.mk) \
+	$(eval .PHONY: $(suite)) \
+	$(eval $(suite): $(compatibility_zip)) \
+	$(eval $(call dist-for-goals, $(suite), $(compatibility_zip))) \
+)
+
 endif
diff --git a/rbesetup.sh b/rbesetup.sh
index 410ed86..ec39e6e 100644
--- a/rbesetup.sh
+++ b/rbesetup.sh
@@ -65,7 +65,10 @@
 function _set_rbe_vars() {
   unset USE_GOMA
   export USE_RBE="true"
-  export RBE_CXX_EXEC_STRATEGY="remote_local_fallback"
+  export RBE_CXX_EXEC_STRATEGY="racing"
+  export RBE_JAVAC_EXEC_STRATEGY="racing"
+  export RBE_R8_EXEC_STRATEGY="racing"
+  export RBE_D8_EXEC_STRATEGY="racing"
   export RBE_use_unified_cas_ops="true"
   export RBE_JAVAC=1
   export RBE_R8=1
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 4d92db7..1b4c8d9 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -323,45 +323,12 @@
     tz_version_host \
     tz_version_host_tzdata_apex \
 
-ifeq ($(ART_APEX_JARS),)
-$(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable)
-endif
-
-# The order matters for runtime class lookup performance.
-PRODUCT_BOOT_JARS := \
-    $(ART_APEX_JARS) \
-    framework-minus-apex \
-    ext \
-    com.android.i18n:core-icu4j \
-    telephony-common \
-    voip-common \
-    ims-common
-
-PRODUCT_UPDATABLE_BOOT_JARS := \
-    com.android.conscrypt:conscrypt \
-    com.android.media:updatable-media \
-    com.android.mediaprovider:framework-mediaprovider \
-    com.android.os.statsd:framework-statsd \
-    com.android.permission:framework-permission \
-    com.android.sdkext:framework-sdkextensions \
-    com.android.wifi:framework-wifi \
-    com.android.tethering:framework-tethering
 
 PRODUCT_COPY_FILES += \
     system/core/rootdir/init.usb.rc:system/etc/init/hw/init.usb.rc \
     system/core/rootdir/init.usb.configfs.rc:system/etc/init/hw/init.usb.configfs.rc \
     system/core/rootdir/etc/hosts:system/etc/hosts
 
-# Add the compatibility library that is needed when android.test.base
-# is removed from the bootclasspath.
-# Default to excluding android.test.base from the bootclasspath.
-ifneq ($(REMOVE_ATB_FROM_BCP),false)
-PRODUCT_PACKAGES += framework-atb-backward-compatibility
-PRODUCT_BOOT_JARS += framework-atb-backward-compatibility
-else
-PRODUCT_BOOT_JARS += android.test.base
-endif
-
 PRODUCT_COPY_FILES += system/core/rootdir/init.zygote32.rc:system/etc/init/hw/init.zygote32.rc
 PRODUCT_SYSTEM_PROPERTIES += ro.zygote?=zygote32
 
@@ -422,4 +389,5 @@
 PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,\
     frameworks/base/config/dirty-image-objects:system/etc/dirty-image-objects)
 
+$(call inherit-product, $(SRC_TARGET_DIR)/product/bootclasspath.mk)
 $(call inherit-product, $(SRC_TARGET_DIR)/product/runtime_libart.mk)
diff --git a/target/product/bootclasspath.mk b/target/product/bootclasspath.mk
new file mode 100644
index 0000000..60dd071
--- /dev/null
+++ b/target/product/bootclasspath.mk
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# 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.
+#
+
+ifeq ($(ART_APEX_JARS),)
+  $(error ART_APEX_JARS is empty; cannot initialize PRODUCT_BOOT_JARS variable)
+endif
+
+# The order matters for runtime class lookup performance.
+PRODUCT_BOOT_JARS := \
+    $(ART_APEX_JARS) \
+    framework-minus-apex \
+    ext \
+    com.android.i18n:core-icu4j \
+    telephony-common \
+    voip-common \
+    ims-common
+
+PRODUCT_UPDATABLE_BOOT_JARS := \
+    com.android.conscrypt:conscrypt \
+    com.android.media:updatable-media \
+    com.android.mediaprovider:framework-mediaprovider \
+    com.android.os.statsd:framework-statsd \
+    com.android.permission:framework-permission \
+    com.android.sdkext:framework-sdkextensions \
+    com.android.wifi:framework-wifi \
+    com.android.tethering:framework-tethering
+
+# Add the compatibility library that is needed when android.test.base
+# is removed from the bootclasspath.
+# Default to excluding android.test.base from the bootclasspath.
+ifneq ($(REMOVE_ATB_FROM_BCP),false)
+  PRODUCT_PACKAGES += framework-atb-backward-compatibility
+  PRODUCT_BOOT_JARS += framework-atb-backward-compatibility
+else
+  PRODUCT_BOOT_JARS += android.test.base
+endif
diff --git a/target/product/virtual_ab_ota/README.md b/target/product/virtual_ab_ota/README.md
index c1d0d7d..2d40c03 100644
--- a/target/product/virtual_ab_ota/README.md
+++ b/target/product/virtual_ab_ota/README.md
@@ -8,6 +8,8 @@
 launch.mk
   |- retrofit.mk
   |- plus_non_ab.mk
+
+launch_with_vendor_ramdisk.mk
   |- compression.mk
 
 compression_retrofit.mk
diff --git a/target/product/virtual_ab_ota/compression.mk b/target/product/virtual_ab_ota/compression.mk
index 2251187..1dd8643 100644
--- a/target/product/virtual_ab_ota/compression.mk
+++ b/target/product/virtual_ab_ota/compression.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch.mk)
+$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk)
 
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true
 
diff --git a/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
new file mode 100644
index 0000000..bc81b33
--- /dev/null
+++ b/target/product/virtual_ab_ota/launch_with_vendor_ramdisk.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2020 The Android Open-Source Project
+#
+# 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.
+#
+
+# Devices launching with Virtual A/B and has a vendor_boot partition is
+# preferred to inherit from this makefile instead of launch.mk.
+
+PRODUCT_VIRTUAL_AB_OTA := true
+
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.enabled=true
+
+PRODUCT_PACKAGES += \
+    linker.vendor_ramdisk \
+    e2fsck.vendor_ramdisk \
diff --git a/tools/fs_config/fs_config.go b/tools/fs_config/fs_config.go
index 16bcefa..4c324fb 100644
--- a/tools/fs_config/fs_config.go
+++ b/tools/fs_config/fs_config.go
@@ -48,10 +48,10 @@
 		path := android.PathForModuleGen(ctx, "empty")
 		t.paths = android.Paths{path}
 
-		rule := android.NewRuleBuilder()
+		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Command().Text("rm -rf").Output(path)
 		rule.Command().Text("touch").Output(path)
-		rule.Build(pctx, ctx, "fs_config_empty", "create empty file")
+		rule.Build("fs_config_empty", "create empty file")
 	}
 }
 
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 11fb584..cafc2bb 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -435,6 +435,7 @@
     ],
     required: [
         "checkvintf",
+        "host_init_verifier",
     ],
     target: {
         darwin: {
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 149deda..bae0b20 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1102,6 +1102,29 @@
   return merged_dict
 
 
+def PartitionMapFromTargetFiles(target_files_dir):
+  """Builds a map from partition -> path within an extracted target files directory."""
+  # Keep possible_subdirs in sync with build/make/core/board_config.mk.
+  possible_subdirs = {
+      "system": ["SYSTEM"],
+      "vendor": ["VENDOR", "SYSTEM/vendor"],
+      "product": ["PRODUCT", "SYSTEM/product"],
+      "system_ext": ["SYSTEM_EXT", "SYSTEM/system_ext"],
+      "odm": ["ODM", "VENDOR/odm", "SYSTEM/vendor/odm"],
+      "vendor_dlkm": [
+          "VENDOR_DLKM", "VENDOR/vendor_dlkm", "SYSTEM/vendor/vendor_dlkm"
+      ],
+      "odm_dlkm": ["ODM_DLKM", "VENDOR/odm_dlkm", "SYSTEM/vendor/odm_dlkm"],
+  }
+  partition_map = {}
+  for partition, subdirs in possible_subdirs.items():
+    for subdir in subdirs:
+      if os.path.exists(os.path.join(target_files_dir, subdir)):
+        partition_map[partition] = subdir
+        break
+  return partition_map
+
+
 def SharedUidPartitionViolations(uid_dict, partition_groups):
   """Checks for APK sharedUserIds that cross partition group boundaries.
 
@@ -1134,6 +1157,36 @@
   return errors
 
 
+def RunHostInitVerifier(product_out, partition_map):
+  """Runs host_init_verifier on the init rc files within partitions.
+
+  host_init_verifier searches the etc/init path within each partition.
+
+  Args:
+    product_out: PRODUCT_OUT directory, containing partition directories.
+    partition_map: A map of partition name -> relative path within product_out.
+  """
+  allowed_partitions = ("system", "system_ext", "product", "vendor", "odm")
+  cmd = ["host_init_verifier"]
+  for partition, path in partition_map.items():
+    if partition not in allowed_partitions:
+      raise ExternalError("Unable to call host_init_verifier for partition %s" %
+                          partition)
+    cmd.extend(["--out_%s" % partition, os.path.join(product_out, path)])
+    # Add --property-contexts if the file exists on the partition.
+    property_contexts = "%s_property_contexts" % (
+        "plat" if partition == "system" else partition)
+    property_contexts_path = os.path.join(product_out, path, "etc", "selinux",
+                                          property_contexts)
+    if os.path.exists(property_contexts_path):
+      cmd.append("--property-contexts=%s" % property_contexts_path)
+    # Add the passwd file if the file exists on the partition.
+    passwd_path = os.path.join(product_out, path, "etc", "passwd")
+    if os.path.exists(passwd_path):
+      cmd.extend(["-p", passwd_path])
+  return RunAndCheckOutput(cmd)
+
+
 def AppendAVBSigningArgs(cmd, partition):
   """Append signing arguments for avbtool."""
   # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index b1b4a16..9360d7b 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -951,18 +951,15 @@
   if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
     raise RuntimeError('Incompatible VINTF metadata')
 
+  partition_map = common.PartitionMapFromTargetFiles(
+      output_target_files_temp_dir)
+
   # Generate and check for cross-partition violations of sharedUserId
   # values in APKs. This requires the input target-files packages to contain
   # *.apk files.
   shareduid_violation_modules = os.path.join(
       output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
   with open(shareduid_violation_modules, 'w') as f:
-    framework_partitions = item_list_to_partition_set(framework_item_list)
-    vendor_partitions = item_list_to_partition_set(vendor_item_list)
-
-    partition_map = {}
-    for partition in (framework_partitions.union(vendor_partitions)):
-      partition_map[partition.lower()] = partition.upper()
     violation = find_shareduid_violation.FindShareduidViolation(
         output_target_files_temp_dir, partition_map)
 
@@ -970,6 +967,8 @@
     f.write(violation)
 
     # Check for violations across the input builds' partition groups.
+    framework_partitions = item_list_to_partition_set(framework_item_list)
+    vendor_partitions = item_list_to_partition_set(vendor_item_list)
     shareduid_errors = common.SharedUidPartitionViolations(
         json.loads(violation), [framework_partitions, vendor_partitions])
     if shareduid_errors:
@@ -978,6 +977,17 @@
       raise ValueError('sharedUserId APK error. See %s' %
                        shareduid_violation_modules)
 
+  # Run host_init_verifier on the combined init rc files.
+  filtered_partitions = {
+      partition: path
+      for partition, path in partition_map.items()
+      # host_init_verifier checks only the following partitions:
+      if partition in ['system', 'system_ext', 'product', 'vendor', 'odm']
+  }
+  common.RunHostInitVerifier(
+      product_out=output_target_files_temp_dir,
+      partition_map=filtered_partitions)
+
   generate_images(output_target_files_temp_dir, rebuild_recovery)
 
   generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index 0b368f9..ecd759c 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -996,6 +996,27 @@
         },
         sparse_image.file_map)
 
+  def test_PartitionMapFromTargetFiles(self):
+    target_files_dir = common.MakeTempDir()
+    os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
+    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
+    os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
+    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
+    os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
+    os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
+    partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
+    self.assertDictEqual(
+        partition_map,
+        {
+            'system': 'SYSTEM',
+            'vendor': 'SYSTEM/vendor',
+            # Prefer PRODUCT over SYSTEM/product
+            'product': 'PRODUCT',
+            'odm': 'SYSTEM/vendor/odm',
+            'vendor_dlkm': 'VENDOR_DLKM',
+            # No system_ext or odm_dlkm
+        })
+
   def test_SharedUidPartitionViolations(self):
     uid_dict = {
         'android.uid.phone': {
diff --git a/tools/signapk/Android.bp b/tools/signapk/Android.bp
index f5b2ba7..b90f010 100644
--- a/tools/signapk/Android.bp
+++ b/tools/signapk/Android.bp
@@ -27,7 +27,7 @@
         "conscrypt-unbundled",
     ],
 
-    required: ["libconscrypt_openjdk_jni"],
+    jni_libs: ["libconscrypt_openjdk_jni"],
 
     // The post-build signing tools need signapk.jar (and its shared libraries,
     // handled in their own Android.bp files)
diff --git a/tools/warn/html_writer.py b/tools/warn/html_writer.py
index b8d3fe6..026a6d0 100644
--- a/tools/warn/html_writer.py
+++ b/tools/warn/html_writer.py
@@ -359,6 +359,25 @@
   csvwriter.writerow([total, '', 'All warnings'])
 
 
+def dump_csv_with_description(csvwriter, warning_records, warning_messages,
+                              warn_patterns, project_names):
+  """Outputs all the warning messages by project."""
+  csv_output = []
+  for record in warning_records:
+    project_name = project_names[record[1]]
+    pattern = warn_patterns[record[0]]
+    severity = pattern['severity'].header
+    category = pattern['category']
+    description = pattern['description']
+    warning = warning_messages[record[2]]
+    csv_output.append([project_name, severity,
+                       category, description,
+                       warning])
+  csv_output = sorted(csv_output)
+  for output in csv_output:
+    csvwriter.writerow(output)
+
+
 # Return s with escaped backslash and quotation characters.
 def escape_string(s):
   return s.replace('\\', '\\\\').replace('"', '\\"')
@@ -666,6 +685,12 @@
     with open(flags.csvpath, 'w') as f:
       dump_csv(csv.writer(f, lineterminator='\n'), warn_patterns)
 
+  if flags.csvwithdescription:
+    with open(flags.csvwithdescription, 'w') as f:
+      dump_csv_with_description(csv.writer(f, lineterminator='\n'),
+                                warning_records, warning_messages,
+                                warn_patterns, project_names)
+
   if flags.gencsv:
     dump_csv(csv.writer(sys.stdout, lineterminator='\n'), warn_patterns)
   else:
diff --git a/tools/warn/warn_common.py b/tools/warn/warn_common.py
index 68ed995..b2dd8ab 100755
--- a/tools/warn/warn_common.py
+++ b/tools/warn/warn_common.py
@@ -77,6 +77,9 @@
                       help='Save CSV warning file to the passed path')
   parser.add_argument('--gencsv', action='store_true',
                       help='Generate CSV file with number of various warnings')
+  parser.add_argument('--csvwithdescription', default='',
+                      help="""Save CSV warning file to the passed path this csv
+                            will contain all the warning descriptions""")
   parser.add_argument('--byproject', action='store_true',
                       help='Separate warnings in HTML output by project names')
   parser.add_argument('--url', default='',