Merge "Handle Clang's change of defaults from -fcommon to -fno-common" into main
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 5f7b382..6c9d903 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -82,15 +82,25 @@
 		os.Exit(1)
 	}
 	var bp2buildFiles []BazelFile
+	productConfig, err := createProductConfigFiles(ctx, res.metrics)
 	ctx.Context().EventHandler.Do("CreateBazelFile", func() {
-		bp2buildFiles = CreateBazelFiles(nil, res.buildFileToTargets, ctx.mode)
+		allTargets := make(map[string]BazelTargets)
+		for k, v := range res.buildFileToTargets {
+			allTargets[k] = append(allTargets[k], v...)
+		}
+		for k, v := range productConfig.bp2buildTargets {
+			allTargets[k] = append(allTargets[k], v...)
+		}
+		bp2buildFiles = CreateBazelFiles(nil, allTargets, ctx.mode)
 	})
-	injectionFiles, additionalBp2buildFiles, err := CreateSoongInjectionDirFiles(ctx, res.metrics)
+	bp2buildFiles = append(bp2buildFiles, productConfig.bp2buildFiles...)
+	injectionFiles, err := createSoongInjectionDirFiles(ctx, res.metrics)
 	if err != nil {
 		fmt.Printf("%s\n", err.Error())
 		os.Exit(1)
 	}
-	bp2buildFiles = append(bp2buildFiles, additionalBp2buildFiles...)
+	injectionFiles = append(injectionFiles, productConfig.injectionFiles...)
+
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 	// Delete files under the bp2build root which weren't just written. An
 	// alternative would have been to delete the whole directory and write these
@@ -109,26 +119,6 @@
 	return &res.metrics
 }
 
-// Wrapper function that will be responsible for all files in soong_injection directory
-// This includes
-// 1. config value(s) that are hardcoded in Soong
-// 2. product_config variables
-func CreateSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, []BazelFile, error) {
-	var ret []BazelFile
-
-	productConfigInjectionFiles, productConfigBp2BuildDirFiles, err := CreateProductConfigFiles(ctx, metrics)
-	if err != nil {
-		return nil, nil, err
-	}
-	ret = append(ret, productConfigInjectionFiles...)
-	injectionFiles, err := soongInjectionFiles(ctx.Config(), metrics)
-	if err != nil {
-		return nil, nil, err
-	}
-	ret = append(injectionFiles, ret...)
-	return ret, productConfigBp2BuildDirFiles, nil
-}
-
 // Get the output directory and create it if it doesn't exist.
 func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
 	dirPath := outputDir.Join(ctx, dir)
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 20355d7..50b8358 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -12,14 +12,19 @@
 	"android/soong/android/soongconfig"
 	"android/soong/starlark_import"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 	"go.starlark.net/starlark"
 )
 
-func CreateProductConfigFiles(
+type createProductConfigFilesResult struct {
+	injectionFiles  []BazelFile
+	bp2buildFiles   []BazelFile
+	bp2buildTargets map[string]BazelTargets
+}
+
+func createProductConfigFiles(
 	ctx *CodegenContext,
-	metrics CodegenMetrics) ([]BazelFile, []BazelFile, error) {
+	metrics CodegenMetrics) (createProductConfigFilesResult, error) {
 	cfg := &ctx.config
 	targetProduct := "unknown"
 	if cfg.HasDeviceProduct() {
@@ -32,29 +37,22 @@
 		targetBuildVariant = "userdebug"
 	}
 
+	var res createProductConfigFilesResult
+
 	productVariablesFileName := cfg.ProductVariablesFileName
 	if !strings.HasPrefix(productVariablesFileName, "/") {
 		productVariablesFileName = filepath.Join(ctx.topDir, productVariablesFileName)
 	}
 	productVariablesBytes, err := os.ReadFile(productVariablesFileName)
 	if err != nil {
-		return nil, nil, err
+		return res, err
 	}
 	productVariables := android.ProductVariables{}
 	err = json.Unmarshal(productVariablesBytes, &productVariables)
 	if err != nil {
-		return nil, nil, err
+		return res, err
 	}
 
-	// Visit all modules to determine the list of ndk libraries
-	// This list will be used to add additional flags for cc stub generation
-	ndkLibsStringFormatted := []string{}
-	ctx.Context().VisitAllModules(func(m blueprint.Module) {
-		if ctx.Context().ModuleType(m) == "ndk_library" {
-			ndkLibsStringFormatted = append(ndkLibsStringFormatted, fmt.Sprintf(`"%s"`, m.Name())) // name will be `"libc.ndk"`
-		}
-	})
-
 	// TODO(b/249685973): the name is product_config_platforms because product_config
 	// was already used for other files. Deduplicate them.
 	currentProductFolder := fmt.Sprintf("product_config_platforms/products/%s-%s", targetProduct, targetBuildVariant)
@@ -64,25 +62,36 @@
 		"{VARIANT}", targetBuildVariant,
 		"{PRODUCT_FOLDER}", currentProductFolder)
 
-	platformMappingContent, err := platformMappingContent(
-		productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"),
-		&productVariables,
-		ctx.Config().Bp2buildSoongConfigDefinitions,
-		metrics.convertedModulePathMap)
-	if err != nil {
-		return nil, nil, err
-	}
-
 	productsForTestingMap, err := starlark_import.GetStarlarkValue[map[string]map[string]starlark.Value]("products_for_testing")
 	if err != nil {
-		return nil, nil, err
+		return res, err
 	}
 	productsForTesting := android.SortedKeys(productsForTestingMap)
 	for i := range productsForTesting {
 		productsForTesting[i] = fmt.Sprintf("  \"@//build/bazel/tests/products:%s\",", productsForTesting[i])
 	}
 
-	injectionDirFiles := []BazelFile{
+	productLabelsToVariables := make(map[string]*android.ProductVariables)
+	productLabelsToVariables[productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}")] = &productVariables
+	for product, productVariablesStarlark := range productsForTestingMap {
+		productVariables, err := starlarkMapToProductVariables(productVariablesStarlark)
+		if err != nil {
+			return res, err
+		}
+		productLabelsToVariables["@//build/bazel/tests/products:"+product] = &productVariables
+	}
+
+	res.bp2buildTargets = createTargets(productLabelsToVariables)
+
+	platformMappingContent, err := platformMappingContent(
+		productLabelsToVariables,
+		ctx.Config().Bp2buildSoongConfigDefinitions,
+		metrics.convertedModulePathMap)
+	if err != nil {
+		return res, err
+	}
+
+	res.injectionFiles = []BazelFile{
 		newFile(
 			currentProductFolder,
 			"soong.variables.bzl",
@@ -164,30 +173,21 @@
 			productReplacer.Replace(`
 build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
 `)),
-		newFile(
-			"cc_toolchain",
-			"ndk_libs.bzl",
-			fmt.Sprintf("ndk_libs = [%v]", strings.Join(ndkLibsStringFormatted, ", ")),
-		),
 	}
-	bp2buildDirFiles := []BazelFile{
+	res.bp2buildFiles = []BazelFile{
 		newFile(
 			"",
 			"platform_mappings",
 			platformMappingContent),
 	}
-	return injectionDirFiles, bp2buildDirFiles, nil
+
+	return res, nil
 }
 
 func platformMappingContent(
-	mainProductLabel string,
-	mainProductVariables *android.ProductVariables,
+	productLabelToVariables map[string]*android.ProductVariables,
 	soongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions,
 	convertedModulePathMap map[string]string) (string, error) {
-	productsForTesting, err := starlark_import.GetStarlarkValue[map[string]map[string]starlark.Value]("products_for_testing")
-	if err != nil {
-		return "", err
-	}
 	var result strings.Builder
 
 	mergedConvertedModulePathMap := make(map[string]string)
@@ -203,13 +203,8 @@
 	}
 
 	result.WriteString("platforms:\n")
-	platformMappingSingleProduct(mainProductLabel, mainProductVariables, soongConfigDefinitions, mergedConvertedModulePathMap, &result)
-	for product, productVariablesStarlark := range productsForTesting {
-		productVariables, err := starlarkMapToProductVariables(productVariablesStarlark)
-		if err != nil {
-			return "", err
-		}
-		platformMappingSingleProduct("@//build/bazel/tests/products:"+product, &productVariables, soongConfigDefinitions, mergedConvertedModulePathMap, &result)
+	for productLabel, productVariables := range productLabelToVariables {
+		platformMappingSingleProduct(productLabel, productVariables, soongConfigDefinitions, mergedConvertedModulePathMap, &result)
 	}
 	return result.String(), nil
 }
@@ -248,7 +243,7 @@
 
 	defaultAppCertificateFilegroup := "//build/bazel/utils:empty_filegroup"
 	if proptools.String(productVariables.DefaultAppCertificate) != "" {
-		defaultAppCertificateFilegroup = "@//" + filepath.Dir(proptools.String(productVariables.DefaultAppCertificate)) + ":android_certificate_directory"
+		defaultAppCertificateFilegroup = "@//" + filepath.Dir(proptools.String(productVariables.DefaultAppCertificate)) + ":generated_android_certificate_directory"
 	}
 
 	for _, suffix := range bazelPlatformSuffixes {
@@ -419,3 +414,33 @@
 
 	return result, nil
 }
+
+func createTargets(productLabelsToVariables map[string]*android.ProductVariables) map[string]BazelTargets {
+	res := make(map[string]BazelTargets)
+	var allDefaultAppCertificateDirs []string
+	for _, productVariables := range productLabelsToVariables {
+		if proptools.String(productVariables.DefaultAppCertificate) != "" {
+			d := filepath.Dir(proptools.String(productVariables.DefaultAppCertificate))
+			if !android.InList(d, allDefaultAppCertificateDirs) {
+				allDefaultAppCertificateDirs = append(allDefaultAppCertificateDirs, d)
+			}
+		}
+	}
+	for _, dir := range allDefaultAppCertificateDirs {
+		content := fmt.Sprintf(ruleTargetTemplate, "filegroup", "generated_android_certificate_directory", propsToAttributes(map[string]string{
+			"srcs": `glob([
+        "*.pk8",
+        "*.pem",
+        "*.avbpubkey",
+    ])`,
+			"visibility": `["//visibility:public"]`,
+		}))
+		res[dir] = append(res[dir], BazelTarget{
+			name:        "generated_android_certificate_directory",
+			packageName: dir,
+			content:     content,
+			ruleClass:   "filegroup",
+		})
+	}
+	return res
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index da4b5cf..c697235 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -15,6 +15,7 @@
 	rust_config "android/soong/rust/config"
 	"android/soong/starlark_fmt"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -24,16 +25,28 @@
 	Contents string
 }
 
-// PRIVATE: Use CreateSoongInjectionDirFiles instead
-func soongInjectionFiles(cfg android.Config, metrics CodegenMetrics) ([]BazelFile, error) {
+// createSoongInjectionDirFiles returns most of the files to write to the soong_injection directory.
+// Some other files also come from CreateProductConfigFiles
+func createSoongInjectionDirFiles(ctx *CodegenContext, metrics CodegenMetrics) ([]BazelFile, error) {
+	cfg := ctx.Config()
 	var files []BazelFile
 
 	files = append(files, newFile("android", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("android", "constants.bzl", android.BazelCcToolchainVars(cfg)))
 
+	// Visit all modules to determine the list of ndk libraries
+	// This list will be used to add additional flags for cc stub generation
+	ndkLibsStringFormatted := []string{}
+	ctx.Context().VisitAllModules(func(m blueprint.Module) {
+		if ctx.Context().ModuleType(m) == "ndk_library" {
+			ndkLibsStringFormatted = append(ndkLibsStringFormatted, fmt.Sprintf(`"%s"`, m.Name())) // name will be `"libc.ndk"`
+		}
+	})
+
 	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("cc_toolchain", "config_constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
 	files = append(files, newFile("cc_toolchain", "sanitizer_constants.bzl", cc.BazelCcSanitizerToolchainVars(cfg)))
+	files = append(files, newFile("cc_toolchain", "ndk_libs.bzl", fmt.Sprintf("ndk_libs = [%v]", strings.Join(ndkLibsStringFormatted, ", "))))
 
 	files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
 	files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 89dd38e..6b10077 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -83,7 +83,8 @@
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
 	testConfig := android.TestConfig("", make(map[string]string), "", make(map[string][]byte))
-	files, err := soongInjectionFiles(testConfig, CreateCodegenMetrics())
+	codegenCtx := NewCodegenContext(testConfig, android.NewTestContext(testConfig).Context, Bp2Build, "")
+	files, err := createSoongInjectionDirFiles(codegenCtx, CreateCodegenMetrics())
 	if err != nil {
 		t.Error(err)
 	}
@@ -106,6 +107,10 @@
 		},
 		{
 			dir:      "cc_toolchain",
+			basename: "ndk_libs.bzl",
+		},
+		{
+			dir:      "cc_toolchain",
 			basename: "sanitizer_constants.bzl",
 		},
 		{
@@ -182,15 +187,45 @@
 		},
 	}
 
-	if len(files) != len(expectedFilePaths) {
-		t.Errorf("Expected %d file, got %d", len(expectedFilePaths), len(files))
+	less := func(a bazelFilepath, b bazelFilepath) bool {
+		return a.dir+"/"+a.basename < b.dir+"/"+b.basename
 	}
 
-	for i := range files {
-		actualFile, expectedFile := files[i], expectedFilePaths[i]
+	fileToFilepath := func(a BazelFile) bazelFilepath {
+		return bazelFilepath{basename: a.Basename, dir: a.Dir}
+	}
 
-		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
-			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
+	sort.Slice(expectedFilePaths, func(i, j int) bool {
+		return less(expectedFilePaths[i], expectedFilePaths[j])
+	})
+	sort.Slice(files, func(i, j int) bool {
+		return less(fileToFilepath(files[i]), fileToFilepath(files[j]))
+	})
+
+	i := 0
+	j := 0
+	for i < len(expectedFilePaths) && j < len(files) {
+		expectedFile, actualFile := expectedFilePaths[i], files[j]
+
+		if actualFile.Dir == expectedFile.dir && actualFile.Basename == expectedFile.basename {
+			i++
+			j++
+		} else if less(expectedFile, fileToFilepath(actualFile)) {
+			t.Errorf("Did not find expected file %s/%s", expectedFile.dir, expectedFile.basename)
+			i++
+		} else {
+			t.Errorf("Found unexpected file %s/%s", actualFile.Dir, actualFile.Basename)
+			j++
 		}
 	}
+	for i < len(expectedFilePaths) {
+		expectedFile := expectedFilePaths[i]
+		t.Errorf("Did not find expected file %s/%s", expectedFile.dir, expectedFile.basename)
+		i++
+	}
+	for j < len(files) {
+		actualFile := files[j]
+		t.Errorf("Found unexpected file %s/%s", actualFile.Dir, actualFile.Basename)
+		j++
+	}
 }
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
index 5d6b088..dfef697 100644
--- a/bp2build/java_proto_conversion_test.go
+++ b/bp2build/java_proto_conversion_test.go
@@ -137,3 +137,50 @@
 		},
 	})
 }
+
+func TestJavaLibsAndOnlyProtoSrcs(t *testing.T) {
+	runJavaProtoTestCase(t, Bp2buildTestCase{
+		Description: "java_library that has only proto srcs",
+		Blueprint: `java_library_static {
+    name: "java-protos",
+    srcs: ["a.proto"],
+    libs: ["java-lib"],
+    java_version: "7",
+    sdk_version: "current",
+}
+
+java_library_static {
+    name: "java-lib",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("proto_library", "java-protos_proto", AttrNameToString{
+				"srcs": `["a.proto"]`,
+			}),
+			MakeBazelTarget(
+				"java_lite_proto_library",
+				"java-protos_java_proto_lite",
+				AttrNameToString{
+					"deps":         `[":java-protos_proto"]`,
+					"java_version": `"7"`,
+					"sdk_version":  `"current"`,
+				}),
+			MakeBazelTarget("java_library", "java-protos", AttrNameToString{
+				"exports": `[
+        ":java-protos_java_proto_lite",
+        ":java-lib-neverlink",
+    ]`,
+				"java_version": `"7"`,
+				"sdk_version":  `"current"`,
+			}),
+			MakeNeverlinkDuplicateTargetWithAttrs(
+				"java_library",
+				"java-protos",
+				AttrNameToString{
+					"java_version": `"7"`,
+					"sdk_version":  `"current"`,
+				}),
+		},
+	})
+}
diff --git a/cc/stl.go b/cc/stl.go
index 8f92dcb..a31a585 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -172,6 +172,7 @@
 		// The system STL doesn't have a prebuilt (it uses the system's libstdc++), but it does have
 		// its own includes. The includes are handled in CCBase.Flags().
 		deps.SharedLibs = append([]string{"libstdc++"}, deps.SharedLibs...)
+		deps.HeaderLibs = append([]string{"ndk_system"}, deps.HeaderLibs...)
 	case "ndk_libc++_shared", "ndk_libc++_static":
 		if stl.Properties.SelectedStl == "ndk_libc++_shared" {
 			deps.SharedLibs = append(deps.SharedLibs, stl.Properties.SelectedStl)
@@ -219,8 +220,7 @@
 	case "libstdc++":
 		// Nothing
 	case "ndk_system":
-		ndkSrcRoot := android.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
-		flags.Local.CFlags = append(flags.Local.CFlags, "-isystem "+ndkSrcRoot.String())
+		// Nothing: The exports of ndk_system will be added automatically to the local cflags
 	case "ndk_libc++_shared", "ndk_libc++_static":
 		if ctx.Arch().ArchType == android.Arm {
 			// Make sure the _Unwind_XXX symbols are not re-exported.
diff --git a/java/droidstubs.go b/java/droidstubs.go
index f05ef1f..1d5dd76 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -539,7 +539,7 @@
 
 	// Force metalava to sort overloaded methods by their order in the source code.
 	// See b/285312164 for more information.
-	cmd.FlagWithArg("--api-overloaded-method-order ", "source")
+	cmd.FlagWithArg("--format-defaults ", "overloaded-method-order=source")
 
 	return cmd
 }
diff --git a/java/java.go b/java/java.go
index 99bb1b3..270f456 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1798,6 +1798,7 @@
 		Flag("-jar").
 		Flag("-write_if_changed").
 		Flag("-ignore_missing_files").
+		Flag("-quiet").
 		FlagWithArg("-C ", unzippedSrcJarDir.String()).
 		FlagWithInput("-l ", classFilesList).
 		FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs)
@@ -2885,8 +2886,9 @@
 // depending on the module type.
 type bp2BuildJavaInfo struct {
 	// separates dependencies into dynamic dependencies and static dependencies.
-	DepLabels *javaDependencyLabels
-	hasKotlin bool
+	DepLabels       *javaDependencyLabels
+	hasKotlin       bool
+	onlyProtoInSrcs bool
 }
 
 func javaXsdTargetName(xsd android.XsdConfigBp2buildTargets) string {
@@ -2949,6 +2951,9 @@
 
 	staticDeps.Append(srcPartitions[xsdSrcPartition])
 
+	_, protoInSrcs := srcPartitions[protoSrcPartition]
+	onlyProtoInSrcs := protoInSrcs && len(srcPartitions) == 1
+
 	if !srcPartitions[logtagSrcPartition].IsEmpty() {
 		logtagsLibName := m.Name() + "_logtags"
 		ctx.CreateBazelTargetModule(
@@ -3086,8 +3091,9 @@
 	}
 
 	bp2BuildInfo := &bp2BuildJavaInfo{
-		DepLabels: depLabels,
-		hasKotlin: hasKotlin,
+		DepLabels:       depLabels,
+		hasKotlin:       hasKotlin,
+		onlyProtoInSrcs: onlyProtoInSrcs,
 	}
 
 	return commonAttrs, bp2BuildInfo, true
@@ -3127,16 +3133,29 @@
 	depLabels := bp2BuildInfo.DepLabels
 
 	deps := depLabels.Deps
+	exports := depLabels.StaticDeps
 	if !commonAttrs.Srcs.IsEmpty() {
-		deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
+		deps.Append(exports) // we should only append these if there are sources to use them
 	} else if !deps.IsEmpty() {
-		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
+		if bp2BuildInfo.onlyProtoInSrcs {
+			// java_library does not accept deps when there are no srcs because
+			// there is no compilation happening, but it accepts exports.
+			// bp2build converts this module to 2 java_libraries + java_xx_proto_library + proto_library
+			// the non-empty deps here are not necessary for compiling the protos, in which case
+			// they're unnecessary as deps on the java_library as well since they aren't
+			// being propagated to any dependencies.
+			// so we can put the deps to exports and drop deps here.
+			exports.Append(deps)
+			deps = bazel.LabelListAttribute{}
+		} else {
+			ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
+		}
 	}
 	var props bazel.BazelTargetModuleProperties
 	attrs := &javaLibraryAttributes{
 		javaCommonAttributes: commonAttrs,
 		Deps:                 deps,
-		Exports:              depLabels.StaticDeps,
+		Exports:              exports,
 	}
 	name := m.Name()
 
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index 5231fae..37537ab 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -174,6 +174,7 @@
 	traceFile := flags.String("trace", "", "write trace to file")
 	sha256Checksum := flags.Bool("sha256", false, "add a zip header to each file containing its SHA256 digest")
 	doNotWrite := flags.Bool("n", false, "Nothing is written to disk -- all other work happens")
+	quiet := flags.Bool("quiet", false, "do not print warnings to console")
 
 	flags.Var(&rootPrefix{}, "P", "path prefix within the zip at which to place files")
 	flags.Var(&listFiles{}, "l", "file containing list of files to zip")
@@ -238,6 +239,7 @@
 		IgnoreMissingFiles:       *ignoreMissingFiles,
 		Sha256Checksum:           *sha256Checksum,
 		DoNotWrite:               *doNotWrite,
+		Quiet:                    *quiet,
 	})
 	if err != nil {
 		fmt.Fprintln(os.Stderr, "error:", err.Error())
diff --git a/zip/zip.go b/zip/zip.go
index 30a2ee7..f91a5f2 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -283,6 +283,7 @@
 	IgnoreMissingFiles       bool
 	Sha256Checksum           bool
 	DoNotWrite               bool
+	Quiet                    bool
 
 	Stderr     io.Writer
 	Filesystem pathtools.FileSystem
@@ -340,7 +341,9 @@
 					Err:  os.ErrNotExist,
 				}
 				if args.IgnoreMissingFiles {
-					fmt.Fprintln(z.stderr, "warning:", err)
+					if !args.Quiet {
+						fmt.Fprintln(z.stderr, "warning:", err)
+					}
 				} else {
 					return err
 				}
@@ -357,7 +360,9 @@
 					Err:  os.ErrNotExist,
 				}
 				if args.IgnoreMissingFiles {
-					fmt.Fprintln(z.stderr, "warning:", err)
+					if !args.Quiet {
+						fmt.Fprintln(z.stderr, "warning:", err)
+					}
 				} else {
 					return err
 				}
@@ -368,7 +373,9 @@
 					Err:  syscall.ENOTDIR,
 				}
 				if args.IgnoreMissingFiles {
-					fmt.Fprintln(z.stderr, "warning:", err)
+					if !args.Quiet {
+						fmt.Fprintln(z.stderr, "warning:", err)
+					}
 				} else {
 					return err
 				}